Compare commits
73 Commits
context_de
...
fix-go-git
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c903529e2d | ||
|
|
64ddf430b7 | ||
|
|
0d2e436639 | ||
|
|
ca15145499 | ||
|
|
3274ebd12a | ||
|
|
505b4ef4ce | ||
|
|
a4e2b0dfe6 | ||
|
|
7cf2b00f0b | ||
|
|
d94454b832 | ||
|
|
f4d0bd87f6 | ||
|
|
645d1496d4 | ||
|
|
65ce074981 | ||
|
|
323014d137 | ||
|
|
31262df504 | ||
|
|
a15fe75b6c | ||
|
|
a59bf2d015 | ||
|
|
b36b8917da | ||
|
|
eed4d49665 | ||
|
|
34d35909a4 | ||
|
|
813bec2377 | ||
|
|
97cbfa050c | ||
|
|
44b936a9aa | ||
|
|
0e6a1ab18d | ||
|
|
60a77dab5c | ||
|
|
b041d8be3c | ||
|
|
a73209864c | ||
|
|
a662e26867 | ||
|
|
198c631bd1 | ||
|
|
6c38708c9f | ||
|
|
8c0ffc667d | ||
|
|
b8fc1474f8 | ||
|
|
bcb0c44300 | ||
|
|
e2294e48c4 | ||
|
|
ad7861cd13 | ||
|
|
f17e27f2ef | ||
|
|
77bf52e0b0 | ||
|
|
3819f29412 | ||
|
|
bdbcc665ae | ||
|
|
b3d0eff376 | ||
|
|
070abea0d8 | ||
|
|
d04defdf13 | ||
|
|
738b934f0e | ||
|
|
87f5c740c7 | ||
|
|
1cbc243ca1 | ||
|
|
b944d8a8dd | ||
|
|
6baa8c8a6d | ||
|
|
315af4d76e | ||
|
|
81862bad30 | ||
|
|
a58119ef6a | ||
|
|
be1016400b | ||
|
|
46b16426df | ||
|
|
f873a7e45a | ||
|
|
3afa9f0c44 | ||
|
|
7c0df2ec32 | ||
|
|
c619a02691 | ||
|
|
66236e1888 | ||
|
|
b9b62b2a2e | ||
|
|
31fb9dd532 | ||
|
|
e0da9621a4 | ||
|
|
a1be6d6b59 | ||
|
|
2eaae7bdc3 | ||
|
|
a2111be51e | ||
|
|
9c1deae17e | ||
|
|
5582e2e15d | ||
|
|
f052ff7882 | ||
|
|
aec352df32 | ||
|
|
c1bee27dff | ||
|
|
3c464e3316 | ||
|
|
a71caf080e | ||
|
|
5e9903ef3c | ||
|
|
0adb31360a | ||
|
|
590abfd5bf | ||
|
|
fa5c854d3a |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -24,4 +24,4 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- run: make test
|
- run: make test
|
||||||
- run: make build
|
- run: make xbuild-all
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -7,3 +7,5 @@ pkg/qliksense/qliksense-packr.go
|
|||||||
pkg/qliksense/docker-registry
|
pkg/qliksense/docker-registry
|
||||||
/pkg/qliksense/tests
|
/pkg/qliksense/tests
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
.idea/
|
||||||
191
LICENSE
Normal file
191
LICENSE
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
https://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
|
||||||
|
|
||||||
|
Copyright 2019 QlikTech International AB
|
||||||
|
|
||||||
|
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.
|
||||||
12
Makefile
12
Makefile
@@ -43,8 +43,8 @@ build: clean generate
|
|||||||
go build -ldflags '$(LDFLAGS)' -tags "$(BUILDTAGS)" -o $(BINDIR)/$(MIXIN)$(FILE_EXT) ./cmd/$(MIXIN)
|
go build -ldflags '$(LDFLAGS)' -tags "$(BUILDTAGS)" -o $(BINDIR)/$(MIXIN)$(FILE_EXT) ./cmd/$(MIXIN)
|
||||||
$(MAKE) clean
|
$(MAKE) clean
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test-setup
|
||||||
test: clean generate
|
test-setup: clean generate
|
||||||
ifeq ($(shell ${WHICH} docker-registry 2>${DEVNUL}),)
|
ifeq ($(shell ${WHICH} docker-registry 2>${DEVNUL}),)
|
||||||
$(eval TMP-docker-distribution := $(shell mktemp -d))
|
$(eval TMP-docker-distribution := $(shell mktemp -d))
|
||||||
git clone https://github.com/docker/distribution.git $(TMP-docker-distribution)/docker-distribution
|
git clone https://github.com/docker/distribution.git $(TMP-docker-distribution)/docker-distribution
|
||||||
@@ -52,9 +52,17 @@ ifeq ($(shell ${WHICH} docker-registry 2>${DEVNUL}),)
|
|||||||
cp $(TMP-docker-distribution)/docker-distribution/bin/registry pkg/qliksense/docker-registry
|
cp $(TMP-docker-distribution)/docker-distribution/bin/registry pkg/qliksense/docker-registry
|
||||||
-rm -rf $(TMP-docker-distribution)
|
-rm -rf $(TMP-docker-distribution)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
.PHONY: test-short
|
||||||
|
test-short: test-setup
|
||||||
go test -short -count=1 -tags "$(BUILDTAGS)" -v ./...
|
go test -short -count=1 -tags "$(BUILDTAGS)" -v ./...
|
||||||
$(MAKE) clean
|
$(MAKE) clean
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test: test-setup
|
||||||
|
go test -count=1 -tags "$(BUILDTAGS)" -v ./...
|
||||||
|
$(MAKE) clean
|
||||||
|
|
||||||
xbuild-all: clean generate
|
xbuild-all: clean generate
|
||||||
$(foreach OS, $(SUPPORTED_PLATFORMS), \
|
$(foreach OS, $(SUPPORTED_PLATFORMS), \
|
||||||
$(foreach ARCH, $(SUPPORTED_ARCHES), \
|
$(foreach ARCH, $(SUPPORTED_ARCHES), \
|
||||||
|
|||||||
@@ -42,3 +42,20 @@ func configViewCmd(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
}
|
}
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func configEditCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||||
|
c := &cobra.Command{
|
||||||
|
Use: "edit [context-name]",
|
||||||
|
Short: "Edit the context cr",
|
||||||
|
Long: `edit the context cr. if no context name provided default context will be edited
|
||||||
|
It will open the vim editor unless KUBE_EDITOR is defined`,
|
||||||
|
Example: `qliksense config edit [context-name]`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
if len(args) == 1 {
|
||||||
|
return q.EditCR(args[0])
|
||||||
|
}
|
||||||
|
return q.EditCR("")
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
|
|
||||||
@@ -68,18 +69,30 @@ func setConfigsCmd(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
var (
|
var (
|
||||||
cmd *cobra.Command
|
cmd *cobra.Command
|
||||||
)
|
)
|
||||||
|
base64Encoded := false
|
||||||
cmd = &cobra.Command{
|
cmd = &cobra.Command{
|
||||||
Use: "set-configs",
|
Use: "set-configs",
|
||||||
Short: "set configurations into the qliksense context as key-value pairs",
|
Short: "set configurations into the qliksense context as key-value pairs",
|
||||||
Example: `
|
Example: `
|
||||||
qliksense config set-configs <service_name>.<attribute>="<value>"
|
qliksense config set-configs <service_name>.<attribute>="<value>"
|
||||||
- The above configuration will be displayed in the CR
|
- The above configuration will be displayed in the CR
|
||||||
|
qliksense config set-configs <service_name>.<attribute>="<value" --base64
|
||||||
|
- if the value is base64 encoded
|
||||||
|
echo "something" | base64 | qliksense config set-configs <service_name>.<attribute> --base64
|
||||||
|
- value is coming from input pipe as base64 encoded
|
||||||
|
echo "something" | qliksense config set-configs <service_name>.<attribute>
|
||||||
|
- value is coming from input pipe
|
||||||
|
|
||||||
`,
|
`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return q.SetConfigs(args)
|
if isInputFromPipe() && len(args) == 1 {
|
||||||
|
return q.SetConfigFromReader(args[0], os.Stdin, base64Encoded)
|
||||||
|
}
|
||||||
|
return q.SetConfigs(args, base64Encoded)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
f := cmd.Flags()
|
||||||
|
f.BoolVarP(&base64Encoded, "base64", "", false, "if the arguments value is base64 encoded")
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +101,7 @@ func setSecretsCmd(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
cmd *cobra.Command
|
cmd *cobra.Command
|
||||||
secret bool
|
secret bool
|
||||||
)
|
)
|
||||||
|
base64Encoded := false
|
||||||
cmd = &cobra.Command{
|
cmd = &cobra.Command{
|
||||||
Use: "set-secrets",
|
Use: "set-secrets",
|
||||||
Short: "set secrets configurations into the qliksense context as key-value pairs",
|
Short: "set secrets configurations into the qliksense context as key-value pairs",
|
||||||
@@ -101,13 +114,24 @@ qliksense config set-secrets <service_name>.<attribute>="<value>" --secret=true
|
|||||||
qliksense config set-secrets <service_name>.<attribute>="<value>" --secret=false
|
qliksense config set-secrets <service_name>.<attribute>="<value>" --secret=false
|
||||||
- Encrypt the secret value and display it in the current context
|
- Encrypt the secret value and display it in the current context
|
||||||
- No secret resource is created
|
- No secret resource is created
|
||||||
- The above configuration will be displayed in the CR `,
|
- The above configuration will be displayed in the CR
|
||||||
|
qliksense config set-secrets <service_name>.<attribute>="<value>" --base64
|
||||||
|
- the <value> is base64 encoded
|
||||||
|
echo "something" | base64 | qliksense config set-secrets <service_name>.<attribute> --base64
|
||||||
|
- value coming from input pipe as base64 encoded
|
||||||
|
echo "something" | qliksense config set-secrets <service_name>.<attribute>
|
||||||
|
- value coming from input pipe`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return q.SetSecrets(args, secret)
|
if isInputFromPipe() && len(args) == 1 {
|
||||||
|
return q.SetSecretsFromReader(args[0], os.Stdin, secret, base64Encoded)
|
||||||
|
}
|
||||||
|
return q.SetSecrets(args, secret, base64Encoded)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
f := cmd.Flags()
|
f := cmd.Flags()
|
||||||
f.BoolVar(&secret, "secret", false, "Whether secrets should be encrypted as a Kubernetes Secret resource")
|
f.BoolVar(&secret, "secret", false, "Whether secrets should be encrypted as a Kubernetes Secret resource")
|
||||||
|
f.BoolVarP(&base64Encoded, "base64", "", false, "if the arguments value is base64 encoded")
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ func crdsViewCmd(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
f := c.Flags()
|
f := c.Flags()
|
||||||
f.BoolVarP(&opts.All, "all", "a", false, "Include All CRDs")
|
f.BoolVarP(&opts.All, "all", "", false, "Include All CRDs")
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,6 +37,6 @@ func crdsInstallCmd(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
f := c.Flags()
|
f := c.Flags()
|
||||||
f.BoolVarP(&opts.All, "all", "a", false, "Include All CRDs")
|
f.BoolVarP(&opts.All, "all", "", false, "Include All CRDs")
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,6 @@ func doEnforceEula() {
|
|||||||
fmt.Println(eulaText)
|
fmt.Println(eulaText)
|
||||||
fmt.Print(eulaPrompt)
|
fmt.Print(eulaPrompt)
|
||||||
answer := readRuneFromTty()
|
answer := readRuneFromTty()
|
||||||
fmt.Printf("%v\n", answer)
|
|
||||||
if strings.ToLower(answer) != "y" {
|
if strings.ToLower(answer) != "y" {
|
||||||
fmt.Println(eulaErrorInstruction)
|
fmt.Println(eulaErrorInstruction)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
@@ -98,9 +97,9 @@ func readRuneFromTty() string {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
defer t.Close()
|
defer t.Close()
|
||||||
answer, err := t.ReadRune()
|
answer, err := t.ReadString()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return string(answer)
|
return answer
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,30 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
func fetchCmd(q *qliksense.Qliksense) *cobra.Command {
|
func fetchCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||||
|
opts := &qliksense.FetchCommandOptions{}
|
||||||
c := &cobra.Command{
|
c := &cobra.Command{
|
||||||
Use: "fetch",
|
Use: "fetch",
|
||||||
Short: "fetch a release from qliksense-k8s repo",
|
Short: "fetch a release from qliksense-k8s repo, if version not supplied, will use from context",
|
||||||
Long: `fetch a release from qliksense-k8s repo`,
|
Long: `fetch a release from qliksense-k8s repo, if version not supplied, will use from context`,
|
||||||
Example: `qliksense fetch <version>`,
|
Example: `qliksense fetch [version]`,
|
||||||
Args: func(cmd *cobra.Command, args []string) error {
|
|
||||||
if len(args) != 1 {
|
|
||||||
return errors.New("requires a version (i.e. v1.0.0)")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return q.FetchQK8s(args[0])
|
if len(args) == 1 {
|
||||||
|
opts.Version = args[0]
|
||||||
|
}
|
||||||
|
return q.FetchK8sWithOpts(opts)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
f := c.Flags()
|
||||||
|
f.StringVarP(&opts.GitUrl, "url", "", "", "git url from where configuration will be pulled")
|
||||||
|
f.StringVarP(&opts.AccessToken, "accessToken", "", "", "access token for git url")
|
||||||
|
f.StringVarP(&opts.SecretName, "secretName", "", "", "kubernetes secret name where a key name accessToken exist")
|
||||||
|
f.BoolVarP(&opts.Overwrite, "overwrite", "", false, "Ovewrite previously fetched veersion as well as local chagnes")
|
||||||
|
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ func preflightCmd(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
return preflightCmd
|
return preflightCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func preflightCheckDnsCmd(q *qliksense.Qliksense) *cobra.Command {
|
func pfDnsCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||||
var preflightDnsCmd = &cobra.Command{
|
var preflightDnsCmd = &cobra.Command{
|
||||||
Use: "dns",
|
Use: "dns",
|
||||||
Short: "perform preflight dns check",
|
Short: "perform preflight dns check",
|
||||||
@@ -28,39 +28,59 @@ func preflightCheckDnsCmd(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
Example: `qliksense preflight dns`,
|
Example: `qliksense preflight dns`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
qp := &preflight.QliksensePreflight{Q: q}
|
qp := &preflight.QliksensePreflight{Q: q}
|
||||||
err := qp.DownloadPreflight()
|
|
||||||
|
// Preflight DNS check
|
||||||
|
fmt.Printf("Preflight DNS check\n")
|
||||||
|
fmt.Println("---------------------")
|
||||||
|
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("There has been an error downloading preflight: %+v", err)
|
fmt.Printf("Preflight DNS check FAILED\n")
|
||||||
log.Println(err)
|
log.Fatal(err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
return qp.CheckDns()
|
if namespace == "" {
|
||||||
|
namespace = "default"
|
||||||
|
}
|
||||||
|
if err = qp.CheckDns(namespace, kubeConfigContents); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
fmt.Print("Preflight DNS check FAILED\n")
|
||||||
|
log.Fatal()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return preflightDnsCmd
|
return preflightDnsCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func preflightCheckK8sVersionCmd(q *qliksense.Qliksense) *cobra.Command {
|
func pfK8sVersionCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||||
var preflightCheckK8sVersionCmd = &cobra.Command{
|
var preflightCheckK8sVersionCmd = &cobra.Command{
|
||||||
Use: "k8s-version",
|
Use: "kube-version",
|
||||||
Short: "check k8s version",
|
Short: "check kubernetes version",
|
||||||
Long: `check minimum valid k8s version on the cluster`,
|
Long: `check minimum valid kubernetes version on the cluster`,
|
||||||
Example: `qliksense preflight k8s-version`,
|
Example: `qliksense preflight kube-version`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
qp := &preflight.QliksensePreflight{Q: q}
|
qp := &preflight.QliksensePreflight{Q: q}
|
||||||
err := qp.DownloadPreflight()
|
|
||||||
|
// Preflight Kubernetes minimum version check
|
||||||
|
fmt.Printf("Preflight kubernetes minimum version check\n")
|
||||||
|
fmt.Println("------------------------------------------")
|
||||||
|
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("There has been an error downloading preflight: %+v", err)
|
fmt.Printf("Preflight kubernetes minimum version check FAILED\n")
|
||||||
log.Println(err)
|
log.Fatal(err)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
return qp.CheckK8sVersion()
|
if err = qp.CheckK8sVersion(namespace, kubeConfigContents); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
fmt.Printf("Preflight kubernetes minimum version check FAILED\n")
|
||||||
|
log.Fatal()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return preflightCheckK8sVersionCmd
|
return preflightCheckK8sVersionCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func preflightAllChecksCmd(q *qliksense.Qliksense) *cobra.Command {
|
func pfAllChecksCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||||
|
var mongodbUrl string
|
||||||
var preflightAllChecksCmd = &cobra.Command{
|
var preflightAllChecksCmd = &cobra.Command{
|
||||||
Use: "all",
|
Use: "all",
|
||||||
Short: "perform all checks",
|
Short: "perform all checks",
|
||||||
@@ -68,14 +88,263 @@ func preflightAllChecksCmd(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
Example: `qliksense preflight all`,
|
Example: `qliksense preflight all`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
qp := &preflight.QliksensePreflight{Q: q}
|
qp := &preflight.QliksensePreflight{Q: q}
|
||||||
err := qp.DownloadPreflight()
|
|
||||||
|
// Preflight run all checks
|
||||||
|
fmt.Printf("Running all preflight checks\n")
|
||||||
|
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("There has been an error downloading preflight: %+v", err)
|
fmt.Println(err)
|
||||||
log.Println(err)
|
fmt.Printf("Running preflight check suite has FAILED...\n")
|
||||||
return err
|
log.Fatal()
|
||||||
}
|
}
|
||||||
return qp.RunAllPreflightChecks()
|
if namespace == "" {
|
||||||
|
namespace = "default"
|
||||||
|
}
|
||||||
|
qp.RunAllPreflightChecks(namespace, kubeConfigContents, mongodbUrl)
|
||||||
|
return nil
|
||||||
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
f := preflightAllChecksCmd.Flags()
|
||||||
|
f.StringVarP(&mongodbUrl, "mongodb-url", "", "", "mongodbUrl to try connecting to")
|
||||||
return preflightAllChecksCmd
|
return preflightAllChecksCmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func pfDeploymentCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||||
|
var pfDeploymentCheckCmd = &cobra.Command{
|
||||||
|
Use: "deployment",
|
||||||
|
Short: "perform preflight deploymwnt check",
|
||||||
|
Long: `perform preflight deployment check to ensure that we can create deployments in the cluster`,
|
||||||
|
Example: `qliksense preflight deployment`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
qp := &preflight.QliksensePreflight{Q: q}
|
||||||
|
|
||||||
|
// Preflight deployments check
|
||||||
|
fmt.Printf("Preflight deployment check\n")
|
||||||
|
fmt.Println("--------------------------")
|
||||||
|
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Preflight deployment check FAILED\n")
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if namespace == "" {
|
||||||
|
namespace = "default"
|
||||||
|
}
|
||||||
|
if err = qp.CheckDeployment(namespace, kubeConfigContents); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
fmt.Print("Preflight deploy check FAILED\n")
|
||||||
|
log.Fatal()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return pfDeploymentCheckCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func pfServiceCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||||
|
var pfServiceCheckCmd = &cobra.Command{
|
||||||
|
Use: "service",
|
||||||
|
Short: "perform preflight service check",
|
||||||
|
Long: `perform preflight service check to ensure that we are able to create services in the cluster`,
|
||||||
|
Example: `qliksense preflight service`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
qp := &preflight.QliksensePreflight{Q: q}
|
||||||
|
|
||||||
|
// Preflight service check
|
||||||
|
fmt.Printf("Preflight service check\n")
|
||||||
|
fmt.Println("-----------------------")
|
||||||
|
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Preflight service check FAILED\n")
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if namespace == "" {
|
||||||
|
namespace = "default"
|
||||||
|
}
|
||||||
|
if err = qp.CheckService(namespace, kubeConfigContents); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
fmt.Print("Preflight service check FAILED\n")
|
||||||
|
log.Fatal()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return pfServiceCheckCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func pfPodCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||||
|
var pfPodCheckCmd = &cobra.Command{
|
||||||
|
Use: "pod",
|
||||||
|
Short: "perform preflight pod check",
|
||||||
|
Long: `perform preflight pod check to ensure we can create pods in the cluster`,
|
||||||
|
Example: `qliksense preflight pod`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
qp := &preflight.QliksensePreflight{Q: q}
|
||||||
|
|
||||||
|
// Preflight pod check
|
||||||
|
fmt.Printf("Preflight pod check\n")
|
||||||
|
fmt.Println("--------------------")
|
||||||
|
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Preflight pod check FAILED\n")
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if namespace == "" {
|
||||||
|
namespace = "default"
|
||||||
|
}
|
||||||
|
if err = qp.CheckPod(namespace, kubeConfigContents); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
fmt.Print("Preflight pod check FAILED\n")
|
||||||
|
log.Fatal()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return pfPodCheckCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func pfCreateRoleCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||||
|
var preflightRoleCmd = &cobra.Command{
|
||||||
|
Use: "role",
|
||||||
|
Short: "preflight create role check",
|
||||||
|
Long: `perform preflight role check to ensure we are able to create a role in the cluster`,
|
||||||
|
Example: `qliksense preflight createRole`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
qp := &preflight.QliksensePreflight{Q: q}
|
||||||
|
|
||||||
|
// Preflight role check
|
||||||
|
fmt.Printf("Preflight role check\n")
|
||||||
|
fmt.Println("---------------------------")
|
||||||
|
namespace, _, err := preflight.InitPreflight()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Preflight role check FAILED\n")
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if err = qp.CheckCreateRole(namespace); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
fmt.Print("Preflight role FAILED\n")
|
||||||
|
log.Fatal()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return preflightRoleCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func pfCreateRoleBindingCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||||
|
var preflightRoleBindingCmd = &cobra.Command{
|
||||||
|
Use: "rolebinding",
|
||||||
|
Short: "preflight create rolebinding check",
|
||||||
|
Long: `perform preflight rolebinding check to ensure we are able to create a rolebinding in the cluster`,
|
||||||
|
Example: `qliksense preflight rolebinding`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
qp := &preflight.QliksensePreflight{Q: q}
|
||||||
|
|
||||||
|
// Preflight createRoleBinding check
|
||||||
|
fmt.Printf("Preflight rolebinding check\n")
|
||||||
|
fmt.Println("---------------------------")
|
||||||
|
namespace, _, err := preflight.InitPreflight()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Preflight rolebinding check FAILED\n")
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if err = qp.CheckCreateRoleBinding(namespace); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
fmt.Print("Preflight rolebinding check FAILED\n")
|
||||||
|
log.Fatal()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return preflightRoleBindingCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func pfCreateServiceAccountCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||||
|
var preflightServiceAccountCmd = &cobra.Command{
|
||||||
|
Use: "serviceaccount",
|
||||||
|
Short: "preflight create ServiceAccount check",
|
||||||
|
Long: `perform preflight serviceaccount check to ensure we are able to create a service account in the cluster`,
|
||||||
|
Example: `qliksense preflight serviceaccount`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
qp := &preflight.QliksensePreflight{Q: q}
|
||||||
|
|
||||||
|
// Preflight createServiceAccount check
|
||||||
|
fmt.Printf("Preflight ServiceAccount check\n")
|
||||||
|
fmt.Println("-------------------------------------")
|
||||||
|
namespace, _, err := preflight.InitPreflight()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Preflight serviceaccount check FAILED\n")
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if err = qp.CheckCreateServiceAccount(namespace); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
fmt.Print("Preflight serviceaccount check FAILED\n")
|
||||||
|
log.Fatal()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return preflightServiceAccountCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func pfCreateAuthCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||||
|
var preflightCreateAuthCmd = &cobra.Command{
|
||||||
|
Use: "authcheck",
|
||||||
|
Short: "preflight authcheck",
|
||||||
|
Long: `perform preflight authcheck that combines the role, rolebinding and serviceaccount checks`,
|
||||||
|
Example: `qliksense preflight authcheck`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
qp := &preflight.QliksensePreflight{Q: q}
|
||||||
|
|
||||||
|
// Preflight authcheck
|
||||||
|
fmt.Printf("Preflight authcheck\n")
|
||||||
|
fmt.Println("------------------------")
|
||||||
|
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Preflight authcheck FAILED\n")
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if err = qp.CheckCreateRB(namespace, kubeConfigContents); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
fmt.Print("Preflight authcheck FAILED\n")
|
||||||
|
log.Fatal()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return preflightCreateAuthCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func pfMongoCheckCmd(q *qliksense.Qliksense) *cobra.Command {
|
||||||
|
var mongodbUrl string
|
||||||
|
var preflightMongoCmd = &cobra.Command{
|
||||||
|
Use: "mongo",
|
||||||
|
Short: "preflight mongo OR preflight mongo --url=<url>",
|
||||||
|
Long: `perform preflight mongo check to ensure we are able to connect to a mongodb instance in the cluster`,
|
||||||
|
Example: `qliksense preflight mongo OR preflight mongo --url=<url>`,
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
qp := &preflight.QliksensePreflight{Q: q}
|
||||||
|
|
||||||
|
// Preflight mongo check
|
||||||
|
fmt.Printf("Preflight mongo check\n")
|
||||||
|
fmt.Println("-------------------------------------")
|
||||||
|
namespace, kubeConfigContents, err := preflight.InitPreflight()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Preflight mongo check FAILED\n")
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if namespace == "" {
|
||||||
|
namespace = "default"
|
||||||
|
}
|
||||||
|
if err = qp.CheckMongo(kubeConfigContents, namespace, mongodbUrl); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
fmt.Print("Preflight mongo check FAILED\n")
|
||||||
|
log.Fatal()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
f := preflightMongoCmd.Flags()
|
||||||
|
f.StringVarP(&mongodbUrl, "url", "", "", "mongodbUrl to try connecting to")
|
||||||
|
return preflightMongoCmd
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,28 +20,7 @@ func pullQliksenseImages(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
return q.PullImages(version, opts.Profile)
|
||||||
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
|
||||||
if version == "" {
|
|
||||||
if qcr, err := qConfig.GetCurrentCR(); err != nil {
|
|
||||||
return err
|
|
||||||
} else {
|
|
||||||
version = qcr.GetLabelFromCr("version")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if version != "" {
|
|
||||||
if !qConfig.IsRepoExistForCurrent(version) {
|
|
||||||
if err := q.FetchQK8s(version); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := qConfig.SwitchCurrentCRToVersionAndProfile(version, opts.Profile); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return q.PullImagesForCurrentCR()
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
f := cmd.Flags()
|
f := cmd.Flags()
|
||||||
@@ -58,7 +37,7 @@ func pushQliksenseImages(q *qliksense.Qliksense) *cobra.Command {
|
|||||||
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||||
if qcr, err := qConfig.GetCurrentCR(); err != nil {
|
if qcr, err := qConfig.GetCurrentCR(); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if registry := qcr.GetImageRegistry(); registry == "" {
|
} else if registry := qcr.Spec.GetImageRegistry(); registry == "" {
|
||||||
return errors.New("no image registry in config")
|
return errors.New("no image registry in config")
|
||||||
} else {
|
} else {
|
||||||
return q.PushImagesForCurrentCR()
|
return q.PushImagesForCurrentCR()
|
||||||
|
|||||||
@@ -100,6 +100,10 @@ func getRootCmd(p *qliksense.Qliksense) *cobra.Command {
|
|||||||
if err := p.SetUpQliksenseDefaultContext(); err != nil {
|
if err := p.SetUpQliksenseDefaultContext(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
pf := api.NewPreflightConfig(p.QliksenseHome)
|
||||||
|
if err := pf.Initialize(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
globalEulaPostRun(cmd, p)
|
globalEulaPostRun(cmd, p)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -185,6 +189,8 @@ func rootCmd(p *qliksense.Qliksense) *cobra.Command {
|
|||||||
// add clean-config-repo-patches command as a sub-command to the app config sub-command
|
// add clean-config-repo-patches command as a sub-command to the app config sub-command
|
||||||
configCmd.AddCommand(cleanConfigRepoPatchesCmd(p))
|
configCmd.AddCommand(cleanConfigRepoPatchesCmd(p))
|
||||||
|
|
||||||
|
// open editor for config
|
||||||
|
configCmd.AddCommand(configEditCmd(p))
|
||||||
// add uninstall command
|
// add uninstall command
|
||||||
cmd.AddCommand(uninstallCmd(p))
|
cmd.AddCommand(uninstallCmd(p))
|
||||||
|
|
||||||
@@ -195,11 +201,17 @@ func rootCmd(p *qliksense.Qliksense) *cobra.Command {
|
|||||||
|
|
||||||
// add preflight command
|
// add preflight command
|
||||||
preflightCmd := preflightCmd(p)
|
preflightCmd := preflightCmd(p)
|
||||||
preflightCmd.AddCommand(preflightCheckDnsCmd(p))
|
preflightCmd.AddCommand(pfDnsCheckCmd(p))
|
||||||
preflightCmd.AddCommand(preflightCheckK8sVersionCmd(p))
|
preflightCmd.AddCommand(pfK8sVersionCheckCmd(p))
|
||||||
preflightCmd.AddCommand(preflightAllChecksCmd(p))
|
preflightCmd.AddCommand(pfAllChecksCmd(p))
|
||||||
//preflightCmd.AddCommand(preflightCheckMongoCmd(p))
|
preflightCmd.AddCommand(pfMongoCheckCmd(p))
|
||||||
//preflightCmd.AddCommand(preflightCheckAllCmd(p))
|
preflightCmd.AddCommand(pfDeploymentCheckCmd(p))
|
||||||
|
preflightCmd.AddCommand(pfServiceCheckCmd(p))
|
||||||
|
preflightCmd.AddCommand(pfPodCheckCmd(p))
|
||||||
|
preflightCmd.AddCommand(pfCreateRoleCheckCmd(p))
|
||||||
|
preflightCmd.AddCommand(pfCreateRoleBindingCheckCmd(p))
|
||||||
|
preflightCmd.AddCommand(pfCreateServiceAccountCheckCmd(p))
|
||||||
|
preflightCmd.AddCommand(pfCreateAuthCheckCmd(p))
|
||||||
|
|
||||||
cmd.AddCommand(preflightCmd)
|
cmd.AddCommand(preflightCmd)
|
||||||
cmd.AddCommand(loadCrFile(p))
|
cmd.AddCommand(loadCrFile(p))
|
||||||
|
|||||||
@@ -29,19 +29,20 @@ The expected output should be similar to the one shown below.
|
|||||||
```console
|
```console
|
||||||
$ qliksense preflight dns
|
$ qliksense preflight dns
|
||||||
|
|
||||||
Creating resources to run preflight checks
|
Preflight DNS check
|
||||||
deployment.apps/qnginx001 created
|
---------------------
|
||||||
service/qnginx001 created
|
Created deployment "dep-dns-preflight-check"
|
||||||
pod/qnginx001-6db5fc95c5-s9sl2 condition met
|
Created service "svc-dns-pf-check"
|
||||||
Running Preflight checks ⠇
|
Created pod: pf-pod-1
|
||||||
--- PASS DNS check
|
Fetching pod: pf-pod-1
|
||||||
--- DNS check passed
|
Fetching pod: pf-pod-1
|
||||||
--- PASS cluster-preflight-checks
|
Exec-ing into the container...
|
||||||
PASS
|
Preflight DNS check: PASSED
|
||||||
|
Completed preflight DNS check
|
||||||
DNS check completed, cleaning up resources now
|
Cleaning up resources...
|
||||||
service "qnginx001" deleted
|
Deleted pod: pf-pod-1
|
||||||
deployment.extensions "qnginx001" deleted
|
Deleted service: svc-dns-pf-check
|
||||||
|
Deleted deployment: dep-dns-preflight-check
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -51,50 +52,202 @@ The command to run this check and the expected similar output are as shown below
|
|||||||
```console
|
```console
|
||||||
$ qliksense preflight k8s-version
|
$ qliksense preflight k8s-version
|
||||||
|
|
||||||
Minimum Kubernetes version supported: 1.11.0
|
Preflight kubernetes minimum version check
|
||||||
Client Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.3", GitCommit:"06ad960bfd03b39c8310aaf92d1e7c12ce618213", GitTreeState:"clean", BuildDate:"2020-02-13T18:08:14Z", GoVersion:"go1.13.8", Compiler:"gc", Platform:"darwin/amd64"}
|
------------------------------------------
|
||||||
Server Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.5", GitCommit:"20c265fef0741dd71a66480e35bd69f18351daea", GitTreeState:"clean", BuildDate:"2019-10-15T19:07:57Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"linux/amd64"}
|
Kubernetes API Server version: v1.15.5
|
||||||
Running Preflight checks ⠇
|
Current K8s Version: 1.15.5
|
||||||
--- PASS Required Kubernetes Version
|
Current 1.15.5 is greater than minimum required version:1.11.0, hence good to go
|
||||||
--- Good to go.
|
Preflight minimum kubernetes version check: PASSED
|
||||||
--- PASS cluster-preflight-checks
|
Completed Preflight kubernetes minimum version check
|
||||||
PASS
|
|
||||||
|
|
||||||
Minimum kubernetes version check completed
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Service check
|
||||||
|
We use the commmand below to test if we are able to create a service in the cluster.
|
||||||
|
```console
|
||||||
|
$ qliksense preflight service
|
||||||
|
|
||||||
|
Preflight service check
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Preflight service check:
|
||||||
|
Created service "svc-pf-check"
|
||||||
|
Preflight service creation check: PASSED
|
||||||
|
Cleaning up resources...
|
||||||
|
Deleted service: svc-pf-check
|
||||||
|
Completed preflight service check
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deployment check
|
||||||
|
We use the commmand below to test if we are able to create a deployment in the cluster. After the test exexutes, we wait until the created deployment terminates before we exit the command.
|
||||||
|
```console
|
||||||
|
$ qliksense preflight deployment
|
||||||
|
|
||||||
|
Preflight deployment check
|
||||||
|
-----------------------
|
||||||
|
Preflight deployment check:
|
||||||
|
Created deployment "deployment-preflight-check"
|
||||||
|
Preflight Deployment check: PASSED
|
||||||
|
Cleaning up resources...
|
||||||
|
Deleted deployment: deployment-preflight-check
|
||||||
|
Completed preflight deployment check
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pod check
|
||||||
|
We use the commmand below to test if we are able to create a pod in the cluster.
|
||||||
|
```console
|
||||||
|
$ qliksense preflight pod
|
||||||
|
|
||||||
|
Preflight pod check
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Preflight pod check:
|
||||||
|
Created pod: pod-pf-check
|
||||||
|
Preflight pod creation check: PASSED
|
||||||
|
Cleaning up resources...
|
||||||
|
Deleted pod: pod-pf-check
|
||||||
|
Completed preflight pod check
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create-Role check
|
||||||
|
We use the command below to test if we are able to create a role in the cluster
|
||||||
|
```shell
|
||||||
|
$ qliksense preflight create-role
|
||||||
|
Preflight create-role check
|
||||||
|
---------------------------
|
||||||
|
Preflight create-role check:
|
||||||
|
Created role: role-preflight-check
|
||||||
|
Preflight create-role check: PASSED
|
||||||
|
Cleaning up resources...
|
||||||
|
Deleted role: role-preflight-check
|
||||||
|
|
||||||
|
Completed preflight create-role check
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create-RoleBinding check
|
||||||
|
We use the command below to test if we are able to create a role binding in the cluster
|
||||||
|
```shell
|
||||||
|
$ qliksense preflight createRoleBinding
|
||||||
|
|
||||||
|
Preflight create roleBinding check
|
||||||
|
---------------------------
|
||||||
|
Preflight createRoleBinding check:
|
||||||
|
Created RoleBinding: role-binding-preflight-check
|
||||||
|
Preflight createRoleBinding check: PASSED
|
||||||
|
Cleaning up resources...
|
||||||
|
Deleting RoleBinding: role-binding-preflight-check
|
||||||
|
Deleted RoleBinding: role-binding-preflight-check
|
||||||
|
|
||||||
|
Completed preflight createRoleBinding check
|
||||||
|
```
|
||||||
|
|
||||||
|
### Create-ServiceAccount check
|
||||||
|
We use the command below to test if we are able to create a service account in the cluster
|
||||||
|
```shell
|
||||||
|
$ qliksense preflight createServiceAccount
|
||||||
|
|
||||||
|
Preflight create ServiceAccount check
|
||||||
|
-------------------------------------
|
||||||
|
Preflight createServiceAccount check:
|
||||||
|
Created Service Account: preflight-check-test-serviceaccount
|
||||||
|
Preflight createServiceAccount check: PASSED
|
||||||
|
Cleaning up resources...
|
||||||
|
Deleting ServiceAccount: preflight-check-test-serviceaccount
|
||||||
|
Deleted ServiceAccount: preflight-check-test-serviceaccount
|
||||||
|
|
||||||
|
Completed preflight createServiceAccount check
|
||||||
|
```
|
||||||
|
|
||||||
|
### CreateRB check
|
||||||
|
We use the command below to combine creation of role, role binding, and service account tests
|
||||||
|
```shell
|
||||||
|
$ qliksense preflight createRB
|
||||||
|
|
||||||
|
Preflight createRB check
|
||||||
|
-------------------------------------
|
||||||
|
Preflight create-role check:
|
||||||
|
Created role: role-preflight-check
|
||||||
|
Preflight create-role check: PASSED
|
||||||
|
Cleaning up resources...
|
||||||
|
Deleted role: role-preflight-check
|
||||||
|
|
||||||
|
Completed preflight create-role check
|
||||||
|
|
||||||
|
Preflight create RoleBinding check:
|
||||||
|
Created RoleBinding: role-binding-preflight-check
|
||||||
|
Preflight create RoleBinding check: PASSED
|
||||||
|
Cleaning up resources...
|
||||||
|
Deleted RoleBinding: role-binding-preflight-check
|
||||||
|
|
||||||
|
Completed preflight create RoleBinding check
|
||||||
|
|
||||||
|
Preflight createServiceAccount check:
|
||||||
|
Created Service Account: preflight-check-test-serviceaccount
|
||||||
|
Preflight createServiceAccount check: PASSED
|
||||||
|
Cleaning up resources...
|
||||||
|
Deleted ServiceAccount: preflight-check-test-serviceaccount
|
||||||
|
|
||||||
|
Completed preflight createServiceAccount check
|
||||||
|
Completed preflight CreateRB check
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mongodb check
|
||||||
|
We can check if we are able to connect to an instance of mongodb on the cluster by either supplying the mongodbUri as part of the command or infer it from the current context.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
qliksense preflight mongo --url=<url> OR
|
||||||
|
qliksense preflight mongo
|
||||||
|
|
||||||
|
Preflight mongo check
|
||||||
|
---------------------
|
||||||
|
Preflight mongodb check:
|
||||||
|
Created pod: pf-mongo-pod
|
||||||
|
stdout: MongoDB shell version v4.2.5
|
||||||
|
connecting to: <url>/?compressors=disabled&gssapiServiceName=mongodb
|
||||||
|
Implicit session: session { "id" : UUID("64f639d3-2c93-4894-80f6-ee14acaf56a5") }
|
||||||
|
MongoDB server version: 4.2.5
|
||||||
|
bye
|
||||||
|
stderr:
|
||||||
|
Preflight mongo check: PASSED
|
||||||
|
Deleted pod: pf-mongo-pod
|
||||||
|
Completed preflight mongodb check
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Running all checks
|
### Running all checks
|
||||||
Run the command shown below to execute all preflight checks.
|
Run the command shown below to execute all preflight checks.
|
||||||
```console
|
```console
|
||||||
|
$ qliksense preflight all --mongodb-url=<url> OR
|
||||||
$ qliksense preflight all
|
$ qliksense preflight all
|
||||||
|
|
||||||
Running all preflight checks
|
Running all preflight checks
|
||||||
|
|
||||||
Running DNS check...
|
Preflight DNS check
|
||||||
Creating resources to run preflight checks
|
-------------------
|
||||||
deployment.apps/qnginx001 created
|
Created deployment "dep-dns-preflight-check"
|
||||||
service/qnginx001 created
|
Created service "svc-dns-pf-check"
|
||||||
pod/qnginx001-6db5fc95c5-grwv2 condition met
|
Created pod: pf-pod-1
|
||||||
Running Preflight checks ⠇
|
Fetching pod: pf-pod-1
|
||||||
--- PASS DNS check
|
Fetching pod: pf-pod-1
|
||||||
--- DNS check passed
|
Exec-ing into the container...
|
||||||
--- PASS cluster-preflight-checks
|
Preflight DNS check: PASSED
|
||||||
PASS
|
Completed preflight DNS check
|
||||||
|
Cleaning up resources...
|
||||||
|
Deleted pod: pf-pod-1
|
||||||
|
Deleted service: svc-dns-pf-check
|
||||||
|
Deleted deployment: dep-dns-preflight-check
|
||||||
|
|
||||||
DNS check completed, cleaning up resources now
|
Preflight kubernetes minimum version check
|
||||||
service "qnginx001" deleted
|
------------------------------------------
|
||||||
deployment.extensions "qnginx001" deleted
|
Kubernetes API Server version: v1.15.5
|
||||||
|
Current K8s Version: 1.15.5
|
||||||
Running minimum kubernetes version check...
|
Current 1.15.5 is greater than minimum required version:1.11.0, hence good to go
|
||||||
Minimum Kubernetes version supported: 1.11.0
|
Preflight minimum kubernetes version check: PASSED
|
||||||
Client Version: version.Info{Major:"1", Minor:"17", GitVersion:"v1.17.3", GitCommit:"06ad960bfd03b39c8310aaf92d1e7c12ce618213", GitTreeState:"clean", BuildDate:"2020-02-13T18:08:14Z", GoVersion:"go1.13.8", Compiler:"gc", Platform:"darwin/amd64"}
|
Completed Preflight kubernetes minimum version check
|
||||||
Server Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.5", GitCommit:"20c265fef0741dd71a66480e35bd69f18351daea", GitTreeState:"clean", BuildDate:"2019-10-15T19:07:57Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"linux/amd64"}
|
...
|
||||||
Running Preflight checks ⠧
|
...
|
||||||
--- PASS Required Kubernetes Version
|
All preflight checks have PASSED
|
||||||
--- Good to go.
|
|
||||||
--- PASS cluster-preflight-checks
|
|
||||||
PASS
|
|
||||||
|
|
||||||
Minimum kubernetes version check completed
|
|
||||||
Completed running all preflight checks
|
Completed running all preflight checks
|
||||||
|
|
||||||
```
|
```
|
||||||
11
go.mod
11
go.mod
@@ -10,12 +10,13 @@ replace (
|
|||||||
k8s.io/client-go => k8s.io/client-go v0.0.0-20191016111102-bec269661e48
|
k8s.io/client-go => k8s.io/client-go v0.0.0-20191016111102-bec269661e48
|
||||||
k8s.io/kubectl => k8s.io/kubectl v0.0.0-20191016120415-2ed914427d51
|
k8s.io/kubectl => k8s.io/kubectl v0.0.0-20191016120415-2ed914427d51
|
||||||
|
|
||||||
sigs.k8s.io/kustomize/api => github.com/qlik-oss/kustomize/api v0.3.3-0.20200206224201-2e697eccbad9
|
sigs.k8s.io/kustomize/api => github.com/qlik-oss/kustomize/api v0.3.3-0.20200402170547-2e8140160c36
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.52.0 // indirect
|
cloud.google.com/go v0.52.0 // indirect
|
||||||
cloud.google.com/go/storage v1.5.0 // indirect
|
cloud.google.com/go/storage v1.5.0 // indirect
|
||||||
|
github.com/Masterminds/semver/v3 v3.0.3
|
||||||
github.com/Shopify/ejson v1.2.1
|
github.com/Shopify/ejson v1.2.1
|
||||||
github.com/aws/aws-sdk-go v1.28.9 // indirect
|
github.com/aws/aws-sdk-go v1.28.9 // indirect
|
||||||
github.com/bugsnag/bugsnag-go v1.5.3 // indirect
|
github.com/bugsnag/bugsnag-go v1.5.3 // indirect
|
||||||
@@ -25,13 +26,13 @@ require (
|
|||||||
github.com/docker/cli v0.0.0-20191212191748-ebca1413117a // indirect
|
github.com/docker/cli v0.0.0-20191212191748-ebca1413117a // indirect
|
||||||
github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7 // indirect
|
github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7 // indirect
|
||||||
github.com/docker/go-metrics v0.0.1 // indirect
|
github.com/docker/go-metrics v0.0.1 // indirect
|
||||||
|
github.com/go-git/go-git/v5 v5.0.0
|
||||||
github.com/gobuffalo/envy v1.9.0 // indirect
|
github.com/gobuffalo/envy v1.9.0 // indirect
|
||||||
github.com/gobuffalo/logger v1.0.3 // indirect
|
github.com/gobuffalo/logger v1.0.3 // indirect
|
||||||
github.com/gobuffalo/packd v1.0.0 // indirect
|
github.com/gobuffalo/packd v1.0.0 // indirect
|
||||||
github.com/gobuffalo/packr/v2 v2.7.1
|
github.com/gobuffalo/packr/v2 v2.7.1
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
|
||||||
github.com/golang/protobuf v1.3.3 // indirect
|
github.com/golang/protobuf v1.3.3 // indirect
|
||||||
github.com/google/uuid v1.1.1
|
|
||||||
github.com/gorilla/mux v1.7.3 // indirect
|
github.com/gorilla/mux v1.7.3 // indirect
|
||||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||||
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
|
github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a
|
||||||
@@ -40,8 +41,9 @@ require (
|
|||||||
github.com/mattn/go-tty v0.0.3
|
github.com/mattn/go-tty v0.0.3
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/morikuni/aec v1.0.0 // indirect
|
github.com/morikuni/aec v1.0.0 // indirect
|
||||||
|
github.com/otiai10/copy v1.1.1
|
||||||
github.com/pkg/errors v0.8.1
|
github.com/pkg/errors v0.8.1
|
||||||
github.com/qlik-oss/k-apis v0.0.22
|
github.com/qlik-oss/k-apis v0.1.1
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/rogpeppe/go-internal v1.5.2 // indirect
|
github.com/rogpeppe/go-internal v1.5.2 // indirect
|
||||||
github.com/spf13/cobra v0.0.6
|
github.com/spf13/cobra v0.0.6
|
||||||
@@ -49,8 +51,7 @@ require (
|
|||||||
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31
|
github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31
|
||||||
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 // indirect
|
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 // indirect
|
||||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a // indirect
|
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a // indirect
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a
|
||||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 // indirect
|
|
||||||
golang.org/x/tools v0.0.0-20200312194400-c312e98713c2 // indirect
|
golang.org/x/tools v0.0.0-20200312194400-c312e98713c2 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20200128133413-58ce757ed39b // indirect
|
google.golang.org/genproto v0.0.0-20200128133413-58ce757ed39b // indirect
|
||||||
google.golang.org/grpc v1.27.0 // indirect
|
google.golang.org/grpc v1.27.0 // indirect
|
||||||
|
|||||||
43
go.sum
43
go.sum
@@ -131,6 +131,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
|
|||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
|
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
|
||||||
|
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
|
||||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||||
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
|
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
|
||||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||||
@@ -205,6 +207,7 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc
|
|||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg=
|
github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg=
|
||||||
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
||||||
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
@@ -297,6 +300,12 @@ github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aev
|
|||||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||||
github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA=
|
github.com/go-critic/go-critic v0.3.5-0.20190904082202-d79a9f0c64db/go.mod h1:+sE8vrLDS2M0pZkBk0wy6+nLdKexVDrl/jBqQOTDThA=
|
||||||
|
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
||||||
|
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
||||||
|
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||||
|
github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
|
||||||
|
github.com/go-git/go-git/v5 v5.0.0 h1:k5RWPm4iJwYtfWoxIJy4wJX9ON7ihPeZZYC1fLYDnpg=
|
||||||
|
github.com/go-git/go-git/v5 v5.0.0/go.mod h1:oYD8y9kWsGINPFJoLdaScGCN6dlKg23blmClfZwtUVA=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||||
@@ -561,6 +570,8 @@ github.com/hashicorp/go-retryablehttp v0.6.3/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER
|
|||||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||||
github.com/hashicorp/go-rootcerts v1.0.1 h1:DMo4fmknnz0E0evoNYnV48RjWndOsmd6OW+09R3cEP8=
|
github.com/hashicorp/go-rootcerts v1.0.1 h1:DMo4fmknnz0E0evoNYnV48RjWndOsmd6OW+09R3cEP8=
|
||||||
github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||||
|
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
|
||||||
|
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
|
||||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||||
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
||||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
||||||
@@ -568,6 +579,7 @@ github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdv
|
|||||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
|
github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=
|
||||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||||
|
github.com/hashicorp/go-version v1.1.0 h1:bPIoEKD27tNdebFGGxxYwcL4nepeY4j1QP23PFRGzg0=
|
||||||
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||||
@@ -662,6 +674,7 @@ github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
|||||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
@@ -755,6 +768,7 @@ github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8m
|
|||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||||
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
|
github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU=
|
||||||
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/nwaples/rardecode v1.0.0 h1:r7vGuS5akxOnR4JQSkko62RJ1ReCMXxQRPtxsiFMBOs=
|
github.com/nwaples/rardecode v1.0.0 h1:r7vGuS5akxOnR4JQSkko62RJ1ReCMXxQRPtxsiFMBOs=
|
||||||
github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
github.com/nwaples/rardecode v1.0.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||||
@@ -787,6 +801,14 @@ github.com/opencontainers/selinux v1.3.0 h1:xsI95WzPZu5exzA6JzkLSfdr/DilzOhCJOqG
|
|||||||
github.com/opencontainers/selinux v1.3.0/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs=
|
github.com/opencontainers/selinux v1.3.0/go.mod h1:+BLncwf63G4dgOzykXAxcmnFlUaOlkDdmw/CqsW6pjs=
|
||||||
github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913 h1:TnbXhKzrTOyuvWrjI8W6pcoI9XPbLHFXCdN2dtUw7Rw=
|
github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913 h1:TnbXhKzrTOyuvWrjI8W6pcoI9XPbLHFXCdN2dtUw7Rw=
|
||||||
github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913/go.mod h1:J6OG6YJVEWopen4avK3VNQSnALmmjvniMmni/YFYAwc=
|
github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913/go.mod h1:J6OG6YJVEWopen4avK3VNQSnALmmjvniMmni/YFYAwc=
|
||||||
|
github.com/otiai10/copy v1.1.1 h1:PH7IFlRQ6Fv9vYmuXbDRLdgTHoP1w483kPNUP2bskpo=
|
||||||
|
github.com/otiai10/copy v1.1.1/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
|
||||||
|
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
||||||
|
github.com/otiai10/curr v1.0.0 h1:TJIWdbX0B+kpNagQrjgq8bCMrbhiuX73M2XwgtDMoOI=
|
||||||
|
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
||||||
|
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
||||||
|
github.com/otiai10/mint v1.3.1 h1:BCmzIS3n71sGfHB5NMNDB3lHYPz8fWSkCAErHed//qc=
|
||||||
|
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
@@ -847,10 +869,14 @@ github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa
|
|||||||
github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
|
github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
|
||||||
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||||
github.com/qlik-oss/k-apis v0.0.22 h1:tntQEeRqDYkBi2Ku5+xt7ABGMeFPck7+DOKrHUnpzwI=
|
github.com/qlik-oss/k-apis v0.0.39 h1:fIGCC7f9kU7319VTSJKr3fLoA9E4MjusRFmOjX3ypis=
|
||||||
github.com/qlik-oss/k-apis v0.0.22/go.mod h1:DNiWYqCqPIN216l7+1rccArNIYPb1Le7kYDcPSyNp+Q=
|
github.com/qlik-oss/k-apis v0.0.39/go.mod h1:yoYGgPJ/H0t9H3NSq64dWfyQY6QWi2L9c+hCJoVO03U=
|
||||||
github.com/qlik-oss/kustomize/api v0.3.3-0.20200206224201-2e697eccbad9 h1:iqeqTS4zjp6rPEaxmFB7pemA2CMjOEN5dYSXZaQ82uw=
|
github.com/qlik-oss/k-apis v0.1.0 h1:uMl1316SNYy5Hm6jy1U7wiCMkut0tKqdP8mBpSuXXp8=
|
||||||
github.com/qlik-oss/kustomize/api v0.3.3-0.20200206224201-2e697eccbad9/go.mod h1:OCt7FTrRVHj4kmR2xLJJUIqu00BTr6GeF09hSmM17Kw=
|
github.com/qlik-oss/k-apis v0.1.0/go.mod h1:yoYGgPJ/H0t9H3NSq64dWfyQY6QWi2L9c+hCJoVO03U=
|
||||||
|
github.com/qlik-oss/k-apis v0.1.1 h1:aZ4eTMB3mSn03Kuj7+RI0eFLkjK9+0qxADBioRb3qVA=
|
||||||
|
github.com/qlik-oss/k-apis v0.1.1/go.mod h1:yoYGgPJ/H0t9H3NSq64dWfyQY6QWi2L9c+hCJoVO03U=
|
||||||
|
github.com/qlik-oss/kustomize/api v0.3.3-0.20200402170547-2e8140160c36 h1:BuT+cnXPQ6mcOWTDS1S8GXy65LAEMdPuNQCC36rMq28=
|
||||||
|
github.com/qlik-oss/kustomize/api v0.3.3-0.20200402170547-2e8140160c36/go.mod h1:tSQaDZ4Jt9KwYvD7LlMUPi5nkiGOno3PAKl5/XqEfxs=
|
||||||
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
|
github.com/quasilyte/go-consistent v0.0.0-20190521200055-c6f3937de18c/go.mod h1:5STLWrekHfjyYwxBRVRXNOSewLJ3PWfDJd1VyTS21fI=
|
||||||
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be h1:ta7tUOvsPHVHGom5hKW5VXNc2xZIkfCKP8iaqOyYtUQ=
|
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be h1:ta7tUOvsPHVHGom5hKW5VXNc2xZIkfCKP8iaqOyYtUQ=
|
||||||
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q=
|
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be/go.mod h1:MIDFMn7db1kT65GmV94GzpX9Qdi7N/pQlwb+AN8wh+Q=
|
||||||
@@ -934,6 +960,8 @@ github.com/spf13/viper v1.6.1 h1:VPZzIkznI1YhVMRi6vNFLHSwhnhReBfgTxIPccpfdZk=
|
|||||||
github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
|
github.com/spf13/viper v1.6.1/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
|
||||||
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
|
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
|
||||||
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
|
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
|
||||||
|
github.com/src-d/go-git v4.7.0+incompatible h1:IYSSnbAHeKmsfbQFi9ozbid+KNh0bKjlorMfQehQbcE=
|
||||||
|
github.com/src-d/go-git v4.7.0+incompatible/go.mod h1:1bQciz+hn0jzPQNsYj0hDFZHLJBdV7gXE2mWhC7EkFk=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||||
@@ -961,6 +989,7 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM
|
|||||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||||
|
github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||||
github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8=
|
github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8=
|
||||||
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||||
github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
|
github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
|
||||||
@@ -995,6 +1024,8 @@ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMx
|
|||||||
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
|
github.com/yujunz/go-getter v1.4.1-lite h1:FhvNc94AXMZkfqUwfMKhnQEC9phkphSGdPTL7tIdhOM=
|
||||||
|
github.com/yujunz/go-getter v1.4.1-lite/go.mod h1:sbmqxXjyLunH1PkF3n7zSlnVeMvmYUuIl9ZVs/7NyCc=
|
||||||
github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940 h1:p7OofyZ509h8DmPLh8Hn+EIIZm/xYhdZHJ9GnXHdr6U=
|
github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940 h1:p7OofyZ509h8DmPLh8Hn+EIIZm/xYhdZHJ9GnXHdr6U=
|
||||||
github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
|
github.com/yvasiyarov/go-metrics v0.0.0-20150112132944-c25f46c4b940/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
|
||||||
github.com/yvasiyarov/gorelic v0.0.6 h1:qMJQYPNdtJ7UNYHjX38KXZtltKTqimMuoQjNnSVIuJg=
|
github.com/yvasiyarov/gorelic v0.0.6 h1:qMJQYPNdtJ7UNYHjX38KXZtltKTqimMuoQjNnSVIuJg=
|
||||||
@@ -1046,6 +1077,7 @@ golang.org/x/crypto v0.0.0-20191112222119-e1110fd1c708/go.mod h1:LzIPMQfyMNhhGPh
|
|||||||
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U=
|
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d h1:9FCpayM9Egr1baVnV1SX0H87m+XB0B8S0hAMi99X/3U=
|
||||||
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 h1:QmwruyY+bKbDDL0BaglrbZABEali68eoMFhTZpCjYVA=
|
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4 h1:QmwruyY+bKbDDL0BaglrbZABEali68eoMFhTZpCjYVA=
|
||||||
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200311171314-f7b00557c8c4/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
@@ -1126,6 +1158,8 @@ golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjut
|
|||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/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-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||||
|
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
@@ -1289,6 +1323,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
|
|||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
|
|||||||
188
pkg/api/apis.go
188
pkg/api/apis.go
@@ -1,11 +1,9 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rsa"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -23,6 +21,7 @@ const (
|
|||||||
qliksenseContextsDirName = "contexts"
|
qliksenseContextsDirName = "contexts"
|
||||||
qliksenseSecretsDirName = "secrets"
|
qliksenseSecretsDirName = "secrets"
|
||||||
qliksenseEjsonDirName = "ejson"
|
qliksenseEjsonDirName = "ejson"
|
||||||
|
QLIK_GIT_REPO = "https://github.com/qlik-oss/qliksense-k8s"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewQConfig create QliksenseConfig object from file ~/.qliksense/config.yaml
|
// NewQConfig create QliksenseConfig object from file ~/.qliksense/config.yaml
|
||||||
@@ -133,6 +132,77 @@ func (qc *QliksenseConfig) GetCRFilePath(contextName string) string {
|
|||||||
}
|
}
|
||||||
return crFilePath
|
return crFilePath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cr *QliksenseCR) IsRepoExist() bool {
|
||||||
|
if cr.Spec.ManifestsRoot == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if _, err := os.Lstat(cr.Spec.ManifestsRoot); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *QliksenseCR) GetFetchUrl() string {
|
||||||
|
if cr.Spec.FetchSource == nil || cr.Spec.FetchSource.Repository == "" {
|
||||||
|
return QLIK_GIT_REPO
|
||||||
|
}
|
||||||
|
return cr.Spec.FetchSource.Repository
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *QliksenseCR) GetFetchAccessToken(encryptionKey string) string {
|
||||||
|
if cr.Spec.FetchSource == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if tok, err := cr.Spec.FetchSource.GetAccessToken(); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return ""
|
||||||
|
} else {
|
||||||
|
by, _ := b64.StdEncoding.DecodeString(tok)
|
||||||
|
res, err := DecryptData(by, encryptionKey)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *QliksenseCR) SetFetchUrl(url string) {
|
||||||
|
if cr.Spec.FetchSource == nil {
|
||||||
|
cr.Spec.FetchSource = &config.Repo{}
|
||||||
|
}
|
||||||
|
cr.Spec.FetchSource.Repository = url
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *QliksenseCR) SetFetchAccessToken(token, encryptionKey string) error {
|
||||||
|
if cr.Spec.FetchSource == nil {
|
||||||
|
cr.Spec.FetchSource = &config.Repo{}
|
||||||
|
}
|
||||||
|
res, err := EncryptData([]byte(token), encryptionKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cr.Spec.FetchSource.AccessToken = b64.StdEncoding.EncodeToString(res)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cr *QliksenseCR) SetFetchAccessSecretName(sec string) {
|
||||||
|
if cr.Spec.FetchSource == nil {
|
||||||
|
cr.Spec.FetchSource = &config.Repo{}
|
||||||
|
}
|
||||||
|
cr.Spec.FetchSource.SecretName = sec
|
||||||
|
}
|
||||||
|
|
||||||
|
//DeleteRepo delete the manifest repo and unset manifestsRoot
|
||||||
|
func (cr *QliksenseCR) DeleteRepo() error {
|
||||||
|
if err := os.RemoveAll(cr.Spec.ManifestsRoot); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cr.Spec.ManifestsRoot = ""
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (qc *QliksenseConfig) IsRepoExist(contextName, version string) bool {
|
func (qc *QliksenseConfig) IsRepoExist(contextName, version string) bool {
|
||||||
if _, err := os.Lstat(qc.BuildRepoPathForContext(contextName, version)); err != nil {
|
if _, err := os.Lstat(qc.BuildRepoPathForContext(contextName, version)); err != nil {
|
||||||
return false
|
return false
|
||||||
@@ -147,6 +217,11 @@ func (qc *QliksenseConfig) IsRepoExistForCurrent(version string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (qc *QliksenseConfig) DeleteRepoForCurrent(version string) error {
|
||||||
|
path := qc.BuildRepoPath(version)
|
||||||
|
return os.RemoveAll(path)
|
||||||
|
}
|
||||||
|
|
||||||
func (qc *QliksenseConfig) BuildRepoPath(version string) string {
|
func (qc *QliksenseConfig) BuildRepoPath(version string) string {
|
||||||
return qc.BuildRepoPathForContext(qc.Spec.CurrentContext, version)
|
return qc.BuildRepoPathForContext(qc.Spec.CurrentContext, version)
|
||||||
}
|
}
|
||||||
@@ -159,10 +234,10 @@ func (qc *QliksenseConfig) BuildCurrentManifestsRoot(version string) string {
|
|||||||
return qc.BuildRepoPath(version)
|
return qc.BuildRepoPath(version)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qc *QliksenseConfig) WriteCR(cr *QliksenseCR, contextName string) error {
|
func (qc *QliksenseConfig) WriteCR(cr *QliksenseCR) error {
|
||||||
crf := qc.GetCRFilePath(contextName)
|
crf := qc.GetCRFilePath(cr.GetName())
|
||||||
if crf == "" {
|
if crf == "" {
|
||||||
return errors.New("context name " + contextName + " not found")
|
return errors.New("context name " + cr.GetName() + " not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
return qc.TransformAndWriteCr(cr, crf)
|
return qc.TransformAndWriteCr(cr, crf)
|
||||||
@@ -183,7 +258,7 @@ func (qc *QliksenseConfig) CreateOrWriteCrAndContext(cr *QliksenseCR) error {
|
|||||||
crf = filepath.Join(cDir, cr.GetName()+".yaml")
|
crf = filepath.Join(cDir, cr.GetName()+".yaml")
|
||||||
ctx := Context{
|
ctx := Context{
|
||||||
Name: cr.GetName(),
|
Name: cr.GetName(),
|
||||||
CrFile: filepath.Join("contexts", cr.GetName(), cr.GetName()+".yaml"),
|
CrFile: "contexts/" + cr.GetName() + "/" + cr.GetName() + ".yaml", //filepath.Join("contexts", cr.GetName(), cr.GetName()+".yaml"),
|
||||||
}
|
}
|
||||||
qc.AddToContexts(ctx)
|
qc.AddToContexts(ctx)
|
||||||
|
|
||||||
@@ -198,6 +273,8 @@ func (qc *QliksenseConfig) CreateOrWriteCrAndContext(cr *QliksenseCR) error {
|
|||||||
func (qc *QliksenseConfig) TransformAndWriteCr(cr *QliksenseCR, file string) error {
|
func (qc *QliksenseConfig) TransformAndWriteCr(cr *QliksenseCR, file string) error {
|
||||||
if strings.HasPrefix(cr.Spec.ManifestsRoot, qc.QliksenseHomePath) {
|
if strings.HasPrefix(cr.Spec.ManifestsRoot, qc.QliksenseHomePath) {
|
||||||
cr.Spec.ManifestsRoot = strings.Replace(cr.Spec.ManifestsRoot, qc.QliksenseHomePath+"/", "", 1)
|
cr.Spec.ManifestsRoot = strings.Replace(cr.Spec.ManifestsRoot, qc.QliksenseHomePath+"/", "", 1)
|
||||||
|
cr.Spec.ManifestsRoot = strings.Replace(cr.Spec.ManifestsRoot, qc.QliksenseHomePath+"\\", "", 1)
|
||||||
|
cr.Spec.ManifestsRoot = strings.Replace(cr.Spec.ManifestsRoot, "\\", "/", -1)
|
||||||
}
|
}
|
||||||
if err := WriteToFile(cr, file); err != nil {
|
if err := WriteToFile(cr, file); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -214,7 +291,7 @@ func (qc *QliksenseConfig) AddToContexts(ctx Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (qc *QliksenseConfig) WriteCurrentContextCR(cr *QliksenseCR) error {
|
func (qc *QliksenseConfig) WriteCurrentContextCR(cr *QliksenseCR) error {
|
||||||
return qc.WriteCR(cr, qc.Spec.CurrentContext)
|
return qc.WriteCR(cr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qc *QliksenseConfig) IsContextExist(ctxName string) bool {
|
func (qc *QliksenseConfig) IsContextExist(ctxName string) bool {
|
||||||
@@ -245,9 +322,9 @@ func (qc *QliksenseConfig) GetCurrentContextSecretsDir() (string, error) {
|
|||||||
func (qc *QliksenseConfig) setDockerConfigJsonSecret(filename string, dockerConfigJsonSecret *DockerConfigJsonSecret) error {
|
func (qc *QliksenseConfig) setDockerConfigJsonSecret(filename string, dockerConfigJsonSecret *DockerConfigJsonSecret) error {
|
||||||
if secretsDir, err := qc.GetCurrentContextSecretsDir(); err != nil {
|
if secretsDir, err := qc.GetCurrentContextSecretsDir(); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if publicKey, _, err := qc.GetCurrentContextEncryptionKeyPair(); err != nil {
|
} else if encryptionKey, err := qc.GetEncryptionKeyForCurrent(); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if dockerConfigJsonSecretYaml, err := dockerConfigJsonSecret.ToYaml(publicKey); err != nil {
|
} else if dockerConfigJsonSecretYaml, err := dockerConfigJsonSecret.ToYaml(encryptionKey); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if err := os.MkdirAll(secretsDir, os.ModePerm); err != nil {
|
} else if err := os.MkdirAll(secretsDir, os.ModePerm); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -294,9 +371,9 @@ func (qc *QliksenseConfig) getDockerConfigJsonSecret(name string) (*DockerConfig
|
|||||||
return nil, err
|
return nil, err
|
||||||
} else if dockerConfigJsonSecretYaml, err := ioutil.ReadFile(filepath.Join(secretsDir, name)); err != nil {
|
} else if dockerConfigJsonSecretYaml, err := ioutil.ReadFile(filepath.Join(secretsDir, name)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if _, privateKey, err := qc.GetCurrentContextEncryptionKeyPair(); err != nil {
|
} else if encryptionKey, err := qc.GetEncryptionKeyForCurrent(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if err := dockerConfigJsonSecret.FromYaml(dockerConfigJsonSecretYaml, privateKey); err != nil {
|
} else if err := dockerConfigJsonSecret.FromYaml(dockerConfigJsonSecretYaml, encryptionKey); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return dockerConfigJsonSecret, nil
|
return dockerConfigJsonSecret, nil
|
||||||
@@ -307,11 +384,11 @@ func (qc *QliksenseConfig) getCurrentContextEncryptionKeyPairLocation() (string,
|
|||||||
if qcr, err := qc.GetCurrentCR(); err != nil {
|
if qcr, err := qc.GetCurrentCR(); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
} else {
|
} else {
|
||||||
return qc.getContextEncryptionKeyPairLocation(qcr.GetName())
|
return qc.getContextEncryptionKeyLocation(qcr.GetName())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qc *QliksenseConfig) getContextEncryptionKeyPairLocation(contextName string) (string, error) {
|
func (qc *QliksenseConfig) getContextEncryptionKeyLocation(contextName string) (string, error) {
|
||||||
// Check env var: QLIKSENSE_KEY_LOCATION to determine location to store keypair
|
// Check env var: QLIKSENSE_KEY_LOCATION to determine location to store keypair
|
||||||
var secretKeyPairLocation string
|
var secretKeyPairLocation string
|
||||||
if os.Getenv("QLIKSENSE_KEY_LOCATION") != "" {
|
if os.Getenv("QLIKSENSE_KEY_LOCATION") != "" {
|
||||||
@@ -321,9 +398,9 @@ func (qc *QliksenseConfig) getContextEncryptionKeyPairLocation(contextName strin
|
|||||||
// QLIKSENSE_KEY_LOCATION has not been set, hence storing key pair in default location:
|
// QLIKSENSE_KEY_LOCATION has not been set, hence storing key pair in default location:
|
||||||
// /.qliksense/secrets/contexts/<current-context>/secrets/
|
// /.qliksense/secrets/contexts/<current-context>/secrets/
|
||||||
secretKeyPairLocation = filepath.Join(qc.QliksenseHomePath, qliksenseSecretsDirName, qliksenseContextsDirName, contextName, qliksenseSecretsDirName)
|
secretKeyPairLocation = filepath.Join(qc.QliksenseHomePath, qliksenseSecretsDirName, qliksenseContextsDirName, contextName, qliksenseSecretsDirName)
|
||||||
|
|
||||||
}
|
}
|
||||||
return secretKeyPairLocation, nil
|
|
||||||
|
return secretKeyPairLocation, os.MkdirAll(secretKeyPairLocation, os.ModePerm)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qc *QliksenseConfig) GetCurrentContextEjsonKeyDir() (string, error) {
|
func (qc *QliksenseConfig) GetCurrentContextEjsonKeyDir() (string, error) {
|
||||||
@@ -338,52 +415,25 @@ func (qc *QliksenseConfig) GetCurrentContextEjsonKeyDir() (string, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qc *QliksenseConfig) GetCurrentContextEncryptionKeyPair() (*rsa.PublicKey, *rsa.PrivateKey, error) {
|
func (qc *QliksenseConfig) GetEncryptionKeyForCurrent() (string, error) {
|
||||||
if qcr, err := qc.GetCurrentCR(); err != nil {
|
if qcr, err := qc.GetCurrentCR(); err != nil {
|
||||||
return nil, nil, err
|
return "", err
|
||||||
} else {
|
} else {
|
||||||
return qc.GetContextEncryptionKeyPair(qcr.GetName())
|
return qc.GetEncryptionKeyFor(qcr.GetName())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (qc *QliksenseConfig) GetContextEncryptionKeyPair(contextName string) (*rsa.PublicKey, *rsa.PrivateKey, error) {
|
func (qc *QliksenseConfig) GetEncryptionKeyFor(contextName string) (string, error) {
|
||||||
secretKeyPairLocation, err := qc.getContextEncryptionKeyPairLocation(contextName)
|
secretKeyLocation, err := qc.getContextEncryptionKeyLocation(contextName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
key, err := LoadSecretKey(secretKeyLocation)
|
||||||
publicKeyFilePath := filepath.Join(secretKeyPairLocation, QliksensePublicKey)
|
if key != "" {
|
||||||
privateKeyFilePath := filepath.Join(secretKeyPairLocation, QliksensePrivateKey)
|
return key, nil
|
||||||
// try to create the dir if it doesn't exist
|
|
||||||
if !FileExists(publicKeyFilePath) || !FileExists(privateKeyFilePath) {
|
|
||||||
LogDebugMessage("Qliksense secretKeyLocation dir does not exist, creating it now: %s", secretKeyPairLocation)
|
|
||||||
if err := os.MkdirAll(secretKeyPairLocation, os.ModePerm); err != nil {
|
|
||||||
err = fmt.Errorf("Not able to create %s dir: %v", secretKeyPairLocation, err)
|
|
||||||
log.Println(err)
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
// generating and storing key-pair
|
|
||||||
err1 := GenerateAndStoreSecretKeypair(secretKeyPairLocation)
|
|
||||||
if err1 != nil {
|
|
||||||
err1 = fmt.Errorf("Not able to generate and store key pair for encryption")
|
|
||||||
log.Println(err1)
|
|
||||||
return nil, nil, err1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if publicKeyBytes, err := ReadKeys(publicKeyFilePath); err != nil {
|
|
||||||
LogDebugMessage("Not able to read public key")
|
|
||||||
return nil, nil, err
|
|
||||||
} else if privateKeyBytes, err := ReadKeys(privateKeyFilePath); err != nil {
|
|
||||||
LogDebugMessage("Not able to read private key")
|
|
||||||
return nil, nil, err
|
|
||||||
} else if rsaPublicKey, err := DecodeToPublicKey(publicKeyBytes); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
} else if rsaPrivateKey, err := DecodeToPrivateKey(privateKeyBytes); err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
} else {
|
|
||||||
return rsaPublicKey, rsaPrivateKey, nil
|
|
||||||
}
|
}
|
||||||
|
fmt.Println("Generating new encryption key for the context: " + contextName)
|
||||||
|
return GenerateAndStoreSecretKey(secretKeyLocation)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cr *QliksenseCR) AddLabelToCr(key, value string) {
|
func (cr *QliksenseCR) AddLabelToCr(key, value string) {
|
||||||
@@ -408,17 +458,6 @@ func (cr *QliksenseCR) GetString() (string, error) {
|
|||||||
return string(out), nil
|
return string(out), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cr *QliksenseCR) GetImageRegistry() string {
|
|
||||||
for _, nameValues := range cr.Spec.Configs {
|
|
||||||
for _, nameValue := range nameValues {
|
|
||||||
if nameValue.Name == "imageRegistry" {
|
|
||||||
return nameValue.Value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cr *QliksenseCR) GetK8sSecretsFolder(qlikSenseHomeDir string) string {
|
func (cr *QliksenseCR) GetK8sSecretsFolder(qlikSenseHomeDir string) string {
|
||||||
return filepath.Join(qlikSenseHomeDir, qliksenseContextsDirName, cr.GetName(), qliksenseSecretsDirName)
|
return filepath.Join(qlikSenseHomeDir, qliksenseContextsDirName, cr.GetName(), qliksenseSecretsDirName)
|
||||||
}
|
}
|
||||||
@@ -440,11 +479,23 @@ func (cr *QliksenseCR) SetEULA(value string) {
|
|||||||
cr.Spec.AddToConfigs("qliksense", "acceptEULA", value)
|
cr.Spec.AddToConfigs("qliksense", "acceptEULA", value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCustomCrdsPath get crds path if exist in the profile dir
|
||||||
|
func (cr *QliksenseCR) GetCustomCrdsPath() string {
|
||||||
|
if cr.Spec.ManifestsRoot == "" || cr.Spec.Profile == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
crdsPath := filepath.Join(cr.Spec.GetManifestsRoot(), "manifests", cr.Spec.Profile, "crds")
|
||||||
|
if _, err := os.Lstat(crdsPath); err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return crdsPath
|
||||||
|
}
|
||||||
|
|
||||||
// GetDecryptedCr it decrypts all the encrypted value and return a new CR
|
// GetDecryptedCr it decrypts all the encrypted value and return a new CR
|
||||||
func (qc *QliksenseConfig) GetDecryptedCr(cr *QliksenseCR) (*QliksenseCR, error) {
|
func (qc *QliksenseConfig) GetDecryptedCr(cr *QliksenseCR) (*QliksenseCR, error) {
|
||||||
newCr := &QliksenseCR{}
|
newCr := &QliksenseCR{}
|
||||||
copier.Copy(newCr, cr)
|
copier.Copy(newCr, cr)
|
||||||
_, rsaPrivateKey, err := qc.GetCurrentContextEncryptionKeyPair()
|
encryptionKey, err := qc.GetEncryptionKeyFor(cr.GetName())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -457,7 +508,7 @@ func (qc *QliksenseConfig) GetDecryptedCr(cr *QliksenseCR) (*QliksenseCR, error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
db, err := Decrypt(b, rsaPrivateKey)
|
db, err := DecryptData(b, encryptionKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -470,6 +521,11 @@ func (qc *QliksenseConfig) GetDecryptedCr(cr *QliksenseCR) (*QliksenseCR, error)
|
|||||||
finalSecrets[k] = newNvs
|
finalSecrets[k] = newNvs
|
||||||
}
|
}
|
||||||
newCr.Spec.Secrets = finalSecrets
|
newCr.Spec.Secrets = finalSecrets
|
||||||
|
|
||||||
|
if newCr.Spec.FetchSource != nil && newCr.Spec.FetchSource.AccessToken != "" {
|
||||||
|
decData := cr.GetFetchAccessToken(encryptionKey)
|
||||||
|
newCr.Spec.FetchSource.AccessToken = decData
|
||||||
|
}
|
||||||
return newCr, nil
|
return newCr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
b64 "encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
@@ -52,12 +53,6 @@ spec:
|
|||||||
qliksense:
|
qliksense:
|
||||||
- name: acceptEULA
|
- name: acceptEULA
|
||||||
value: "yes"
|
value: "yes"
|
||||||
secrets:
|
|
||||||
qliksense:
|
|
||||||
- name: mongoDbUri
|
|
||||||
# this is rsa encrypted value, the pub and pri keys are in setuPublicAndPrivateKey() method
|
|
||||||
# actual value: mongodb://qlik-default-mongodb:27017/qliksense?ssl=false
|
|
||||||
value: n/pDi7Z/A3i16cAHFFwMp19/egNKc8WZxm6MKHLT/B1DMv3U6pDXWyXT5fYYDV1wDTO3Vk43yECST1UgZYmMpgUOwgSfGgqTVi2VqS0JQsnwI+Twwhnvha8RJANX8b/XIoSFVWaOgy7+RP35ZkvOqHdCfC2aT8JMIHgBQqqCbsNgimCuRSxi0klR000ic/Tp5PYSz5mD+WLrkPw2FbS0OVBsQ/hIp5GZrmVpvEOZdbT63Sz+n/G4Br6GTv2LkZcU7JBuKQm2wfB+mRjJmJnNrPawLfn2UZ89Rz0BLwIy+6b24/RoIUgoNowfGkJreGiwItGK8fjCcx11oavK/yAo6pYZXCcru46pmHbxxle1OlkdTKkG6EVtJuKjSZXtVmBHZYRFzsR7HnAiXnL7QzSEcS7ieZlQvTmNLfpidJhK199oSbyKREqXGl2S8DzPKM9RLccVbQTy6X8qWimP3MYCnO4K0KoQnNQAgfuV8ZxnvdDecByLDPIpmFMGy0Xm9pUZWxmSoDBq+p5WBI2HdCX2gCYVv5yxS2iBqO5SMKo8iOglHtPI9NIMvloERdN1vZtxSRkY5uDEfrU9ysYwfayEXxvXmdWv0HxlotcgUinP02j7k+OfIapTmY/jGfvF4euyCGRKuJ9JlSD9pIiRdAcekjL6hCxXLJLdajCV4sL/YDo=
|
|
||||||
`
|
`
|
||||||
ctx1Dir := filepath.Join(homeDir, "contexts", "contx1")
|
ctx1Dir := filepath.Join(homeDir, "contexts", "contx1")
|
||||||
crFile := filepath.Join(ctx1Dir, "contx1.yaml")
|
crFile := filepath.Join(ctx1Dir, "contx1.yaml")
|
||||||
@@ -106,13 +101,16 @@ func TestGetDecryptedCr(t *testing.T) {
|
|||||||
t.Fail()
|
t.Fail()
|
||||||
t.Log(e)
|
t.Log(e)
|
||||||
}
|
}
|
||||||
qcr, err := qct.GetCurrentCR()
|
|
||||||
if err != nil {
|
|
||||||
t.Fail()
|
|
||||||
t.Log(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
setuPublicAndPrivateKey(dir)
|
qcr, err := qct.GetCurrentCR()
|
||||||
|
|
||||||
|
key, _ := setupGenerateKey(dir)
|
||||||
|
ecn, _ := EncryptData([]byte("mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"), key)
|
||||||
|
b := b64.StdEncoding.EncodeToString(ecn)
|
||||||
|
qcr.Spec.AddToSecrets("qliksense", "mongoDbUri", b, "")
|
||||||
|
|
||||||
|
qcr.SetFetchAccessToken("mytoken", key)
|
||||||
|
|
||||||
newCr, err := qct.GetDecryptedCr(qcr)
|
newCr, err := qct.GetDecryptedCr(qcr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
@@ -129,77 +127,12 @@ func TestGetDecryptedCr(t *testing.T) {
|
|||||||
if decryptedValue == orignalValue {
|
if decryptedValue == orignalValue {
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
|
if newCr.Spec.FetchSource.AccessToken != "mytoken" {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
td()
|
td()
|
||||||
}
|
}
|
||||||
func setuPublicAndPrivateKey(homeDir string) ([]byte, []byte, error) {
|
func setupGenerateKey(homeDir string) (string, error) {
|
||||||
privKeyBytes := []byte(`-----BEGIN RSA PRIVATE KEY-----
|
|
||||||
MIIJJwIBAAKCAgEAwUCimKCidbF3UxEHPy8K+hvhklRB9JYhj5sJy0if4lTVibkK
|
|
||||||
1MrYCykOnmC40pPU9GLY1b8HxAg9tvyRn0YHUxOra6vVQaVcOVJhTM8D18d+lSr3
|
|
||||||
Lp1yiX+UGT4nzWI9+R1CCbwXrqeQVoZs6QZKynEXMkFI9/wNMOwPOvQFOSTuoEoC
|
|
||||||
O+zyTyUWEkNbUq825ELUQdIsjgmlWUOONudxsAr7ESRXW9QTHVh6uWmr3VRKZHby
|
|
||||||
1JdU3I/wjdlGg5M2dDuXy5nQO9w/nYLjJXiw+zzOetZ/+t7/VOkOpNTeJQhwTM1W
|
|
||||||
F7Y2VLetbi9FHgyzHatrduh07+XEiTbgDf3GIx2bp2p6oh0G3N2zpiLcK/aZj8ro
|
|
||||||
uWWydfFfsU3MZ4FfJDP8I6b9awxjmKYqIr6hiPQCJaLBED8mwK+I5evIbnKv6E6u
|
|
||||||
K+BApWA/R7ElragoFYbqQ1VpvntVMtJt9Dy5ZrI+IQARdXD3bb34oh0IPBhClnvv
|
|
||||||
MUc1cWxDoXEX6oJ4I+LzxE87Zkwnan9qOwengolMVKFwPx1o37qrbmrXID21kKt7
|
|
||||||
FL6xN4HxHLkItr1fKzdyWDFRHgASTAWfx5BIwvPuUW0vZHkvO80VyV2L63whVhPn
|
|
||||||
PASmFkbviomrBttYfpr2aGQqF/qR1Nlxe834MFxk1pS9LMa/WnzvFr0gWakCAwEA
|
|
||||||
AQKCAgARSp9B2N2wejibDiL/3E23I1eDqFZedDB8kPrHXbAwqDaTJCN79spt9TaB
|
|
||||||
pVXkQaYEV/Pe7EDdoX8kKGU/QxzUqiXkdHOYdBtUZbKfFMbbP9ZrsnR7j0r4UpoF
|
|
||||||
yDH3hprU93E5PcNAtW2M0GpeT1nR01yn+n908PCdOAIE3GC7RDq1zOl2QzVLL55R
|
|
||||||
9ATv2Q2oTvJ/ETc7XlGVMx4+e2cIwXLFjeLjLI6pSYlxnarrGuetJZeEviWxto9n
|
|
||||||
odFVZI6yx8JFTXX8ZTCr/1IjwDDVyhMPmrHI2Lsv9cqBpSpbVe32cUkKxhsGaYjz
|
|
||||||
GvesQKamOPhco2ATNxPm0yopFlPsGKMfVl0BK0J6BqFh1BvU/SYJmXfnFuUNO3vV
|
|
||||||
4u2Saa0q1iddxV0rXDwIqUfn+S6rwzK0G7y8bH2yvpB2VwiG3TFPnULep4wsefNq
|
|
||||||
Fj92kqFBjacGpQLEEslUY0CMgeZ2+NuBQSUTscP3wBRsottMR6YXJtINdvfHBx+e
|
|
||||||
EcN71z8D00w3mYqIQ7qb4Ml6HOqknunn58g43L9sACMUMTlEBXa9pUnScNYgWBAz
|
|
||||||
W2q2mH37cIydM2JRZPpA8B4yTHt5ugJmChwyNFM7941arjKrebH+6AzLkofGedOP
|
|
||||||
zg+vZQuPEXWs+3MBBnkWoyJW3Y0fbQdjsuQTtnd+7iyoxoBroQKCAQEA4dIiFlIS
|
|
||||||
MDfRhQQWSiDvaw9aneDEJ3uo63ZRH5tm/IynLgtjYgEm/ZxlBCQgqRKLYELBxhu8
|
|
||||||
SaF0uPK8pmpFJt0mIwSlsdeVhuE2obQeKUCczaqrKeaHS3PdWLjTlwph81BGRkHy
|
|
||||||
qfqtNylyyMxrdEbnR51EtsWgFq6anTUAui1Q09JMuMNZRMOzDs1F4gExgD22rc0V
|
|
||||||
c9YQ+jHJRxBGtNKMpMEqc8cvaxBidbItrN9SMTSWog7uYPBuEuaJ6K9vpgyJMOzJ
|
|
||||||
SYcQEFGqgIqIDCg+ABE4d/4YROMKZ1DV/bJCind9brUHSx6XALsF0nC5c1Q9TnUL
|
|
||||||
qI2khOwts4KYKwKCAQEA2xRC6Az97Vkdzu7BjLJ1FKmx4S2nEEgVS12ds82U+5Xf
|
|
||||||
BHKAJnjqlqmmpzzJG+d77IYktz0+mey1QCNkqlm2fhuKs8LZMnpZRf0l8VcoBsUP
|
|
||||||
/xKz7wfiE7RRFZtLJhPp4hhe43GzX5/JFMWMnC6UykwQbj4t1E/GNM/Suqwvg12M
|
|
||||||
wktAJ6nqLgfhjQSO4xWo+nPzcbX+fNtrPCZVrBhYXihhcwRRNImWUCGJ6J4LMdPY
|
|
||||||
Y9Z59qhOvE9cReH/Xw1av46omyiSyAqlgPyZ/kzA2IJSqYCjiQR/2+RD/g13jpcJ
|
|
||||||
jatXLVZ8MJSL5OTS40G/HHTNNpNHbKKh0GOyxBA3ewKCAQBAn8UXhCcmW2L/YPsL
|
|
||||||
/b7mcX9qPP+FmRLvR23R0MQ5M/tH5wRq8I969n3GIJykJeVzB8eybQ+GNslTgEvS
|
|
||||||
iAkAJTubu+G7MkndTqg2wHf9MDtvdA8Fr646Po8yq7oJuHPtkKR7yLWsRUu6xIbP
|
|
||||||
xgheP0hCq1QVxhqZQyCGKrvpi7xc0gsYuPbcAfFFJCOCmPrUi1SzCkTAYJt9LjA+
|
|
||||||
wP6rErIjGBCRD4iXaBn1OqdtmH9KC5WsDP/VCBlIGWeQCly2NVIxiSHVg+xp7yUP
|
|
||||||
IhXq/L05gbQaSsIhPKQmivCiaJg4The8TdwneDqYf+0bmxzHT203/bD3bImPbJNr
|
|
||||||
ksz/AoIBAEwu4Y1cZzkQUmNRd5D7xecnk6ngfEYXKwCIT3zlMrfCSEl9n77BMaKu
|
|
||||||
4Dsr0iuX9eosQ7xM2eYhAG6LYEg05lc4MKWOToVVMpI6E+W3Dz47bPKgiF3I+f8s
|
|
||||||
Jz5CQIG/TwfGvciOE3hfUkec4ua09BzdEqGjkcBQ9XYMBxXPJr6h2379OBQS7FKR
|
|
||||||
fwfQ2/dv4tElXTTfut2kV8gU9Jnh5Wjo1epvR+XjKpg28YQo4W+0YX1magcyRB8L
|
|
||||||
4eSTUIC3XiVa8Jr0IwbZXPBb5xkdi7o+p4w2JahSHjxTRqmj+T1mnHXdbXVgq9Mg
|
|
||||||
9Pzl7cgFZvX4UBx4XtASRf73jITNtt0CggEADH9K+O7FrIOSQly0sMvsRCMtejp3
|
|
||||||
o+MDh1Q+vEg2kEgNXjS4ZFVljUpM2kg1OdUz7feS4dLXUJiIQ8ZWtZPedcq7wjHd
|
|
||||||
02he5+s06l0jPifN3tX1ADfXGpXg5R2fbkrIzakkPP5/RO/aDxIUo7qhklNsVTXO
|
|
||||||
VlGGfWLdk0ekA4upKm02Q1+YOlbIcAicEYYY8K7IffUwnohzKwL9yfuGi1VKTXpE
|
|
||||||
4fzdegsHI03FSqR7V+LvtBpIupQ7RO4kuBmCEyI4E9FVknchg4te4gO3qwd9y0rJ
|
|
||||||
Gu7HNIOrwOHzviI7J6Nd/l9MmeKqklHSgJvko/f5TmiXuQQ8xDZf84rcjQ==
|
|
||||||
-----END RSA PRIVATE KEY-----
|
|
||||||
`)
|
|
||||||
|
|
||||||
publicKeyBytes := []byte(`-----BEGIN RSA PUBLIC KEY-----
|
|
||||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwUCimKCidbF3UxEHPy8K
|
|
||||||
+hvhklRB9JYhj5sJy0if4lTVibkK1MrYCykOnmC40pPU9GLY1b8HxAg9tvyRn0YH
|
|
||||||
UxOra6vVQaVcOVJhTM8D18d+lSr3Lp1yiX+UGT4nzWI9+R1CCbwXrqeQVoZs6QZK
|
|
||||||
ynEXMkFI9/wNMOwPOvQFOSTuoEoCO+zyTyUWEkNbUq825ELUQdIsjgmlWUOONudx
|
|
||||||
sAr7ESRXW9QTHVh6uWmr3VRKZHby1JdU3I/wjdlGg5M2dDuXy5nQO9w/nYLjJXiw
|
|
||||||
+zzOetZ/+t7/VOkOpNTeJQhwTM1WF7Y2VLetbi9FHgyzHatrduh07+XEiTbgDf3G
|
|
||||||
Ix2bp2p6oh0G3N2zpiLcK/aZj8rouWWydfFfsU3MZ4FfJDP8I6b9awxjmKYqIr6h
|
|
||||||
iPQCJaLBED8mwK+I5evIbnKv6E6uK+BApWA/R7ElragoFYbqQ1VpvntVMtJt9Dy5
|
|
||||||
ZrI+IQARdXD3bb34oh0IPBhClnvvMUc1cWxDoXEX6oJ4I+LzxE87Zkwnan9qOwen
|
|
||||||
golMVKFwPx1o37qrbmrXID21kKt7FL6xN4HxHLkItr1fKzdyWDFRHgASTAWfx5BI
|
|
||||||
wvPuUW0vZHkvO80VyV2L63whVhPnPASmFkbviomrBttYfpr2aGQqF/qR1Nlxe834
|
|
||||||
MFxk1pS9LMa/WnzvFr0gWakCAwEAAQ==
|
|
||||||
-----END RSA PUBLIC KEY-----
|
|
||||||
`)
|
|
||||||
secretKeyPairDir := filepath.Join(homeDir, "secrets", "contexts", "contx1", "secrets")
|
secretKeyPairDir := filepath.Join(homeDir, "secrets", "contexts", "contx1", "secrets")
|
||||||
if err := os.MkdirAll(secretKeyPairDir, 0777); err != nil {
|
if err := os.MkdirAll(secretKeyPairDir, 0777); err != nil {
|
||||||
err = fmt.Errorf("Not able to create directories")
|
err = fmt.Errorf("Not able to create directories")
|
||||||
@@ -207,19 +140,33 @@ MFxk1pS9LMa/WnzvFr0gWakCAwEAAQ==
|
|||||||
}
|
}
|
||||||
os.Setenv("QLIKSENSE_KEY_LOCATION", secretKeyPairDir)
|
os.Setenv("QLIKSENSE_KEY_LOCATION", secretKeyPairDir)
|
||||||
|
|
||||||
privKeyFile := filepath.Join(secretKeyPairDir, "qliksensePriv")
|
key, _ := LoadSecretKey(secretKeyPairDir)
|
||||||
// construct and write priv key file into secretsDir location
|
|
||||||
err := ioutil.WriteFile(privKeyFile, privKeyBytes, 0777)
|
if key == "" {
|
||||||
if err != nil {
|
return GenerateAndStoreSecretKey(secretKeyPairDir)
|
||||||
log.Printf("Error while creating file: %v", err)
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
}
|
||||||
pubKeyFile := filepath.Join(secretKeyPairDir, "qliksensePub")
|
return key, nil
|
||||||
// construct and write pub key file into secretsDir location
|
}
|
||||||
err = ioutil.WriteFile(pubKeyFile, publicKeyBytes, 0777)
|
|
||||||
if err != nil {
|
func Test_set_and_get_fetch_access_token(t *testing.T) {
|
||||||
log.Printf("Error while creating file: %v", err)
|
td, homeDir := setup()
|
||||||
return nil, nil, err
|
defer td()
|
||||||
}
|
createCRFile(homeDir)
|
||||||
return publicKeyBytes, privKeyBytes, nil
|
crFile := filepath.Join("contexts", "contx1", "contx1.yaml")
|
||||||
|
qConfig := NewQConfig(homeDir)
|
||||||
|
newQ, _ := qConfig.SetCrLocation("contx1", crFile)
|
||||||
|
newQ.Write()
|
||||||
|
qConfig = NewQConfig(homeDir)
|
||||||
|
qcr, _ := qConfig.GetCurrentCR()
|
||||||
|
key, _ := qConfig.GetEncryptionKeyFor(qcr.GetName())
|
||||||
|
if err := qcr.SetFetchAccessToken("mytokenbeforeencryption", key); err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
tok := qcr.GetFetchAccessToken(key)
|
||||||
|
if tok != "mytokenbeforeencryption" {
|
||||||
|
t.Log("Expected: mytokenbeforeencryption, got: " + tok)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
8
pkg/api/copy.go
Normal file
8
pkg/api/copy.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import "github.com/otiai10/copy"
|
||||||
|
|
||||||
|
//copy source directory to destination
|
||||||
|
func CopyDirectory(source string, dest string) error {
|
||||||
|
return copy.Copy(source, dest)
|
||||||
|
}
|
||||||
101
pkg/api/copy_test.go
Normal file
101
pkg/api/copy_test.go
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
kapis_git "github.com/qlik-oss/k-apis/pkg/git"
|
||||||
|
"sigs.k8s.io/kustomize/api/filesys"
|
||||||
|
"sigs.k8s.io/kustomize/api/konfig"
|
||||||
|
"sigs.k8s.io/kustomize/api/krusty"
|
||||||
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCopyDirectory(t *testing.T) {
|
||||||
|
src, _ := ioutil.TempDir("", "")
|
||||||
|
f1, _ := ioutil.TempFile(src, "")
|
||||||
|
ioutil.TempFile(src, "")
|
||||||
|
|
||||||
|
dest, _ := ioutil.TempDir("", "")
|
||||||
|
CopyDirectory(src, dest)
|
||||||
|
if _, err := os.Lstat(filepath.Join(dest, filepath.Base(f1.Name()))); err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopyDirectory_withGit_withKuz(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("Skipping in short test mode")
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpDir1, err := ioutil.TempDir("", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpDir1)
|
||||||
|
|
||||||
|
tmpDir2, err := ioutil.TempDir("", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpDir2)
|
||||||
|
|
||||||
|
repoPath1 := path.Join(tmpDir1, "repo")
|
||||||
|
repo1, err := kapis_git.CloneRepository(repoPath1, "https://github.com/qlik-oss/qliksense-k8s", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := CopyDirectory(repoPath1, tmpDir2); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
repoPath2 := tmpDir2
|
||||||
|
repo2, err := kapis_git.OpenRepository(repoPath2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := kapis_git.Checkout(repo2, "v0.0.2", "", nil); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
repo2Manifest, err := kuz(path.Join(repoPath2, "manifests", "docker-desktop"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := kapis_git.Checkout(repo1, "v0.0.2", "", nil); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
repo1Manifest, err := kuz(path.Join(repoPath1, "manifests", "docker-desktop"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(repo2Manifest) != string(repo1Manifest) {
|
||||||
|
t.Logf("manifest generated on the original config:\n%v", string(repo1Manifest))
|
||||||
|
t.Logf("manifest generated on the copied config:\n%v", string(repo2Manifest))
|
||||||
|
t.Fatal("expected manifests to be equal, but they were not")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func kuz(directory string) ([]byte, error) {
|
||||||
|
options := &krusty.Options{
|
||||||
|
DoLegacyResourceSort: false,
|
||||||
|
LoadRestrictions: types.LoadRestrictionsNone,
|
||||||
|
DoPrune: false,
|
||||||
|
PluginConfig: konfig.DisabledPluginConfig(),
|
||||||
|
}
|
||||||
|
k := krusty.MakeKustomizer(filesys.MakeFsOnDisk(), options)
|
||||||
|
resMap, err := k.Run(directory)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resMap.AsYaml()
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rsa"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
@@ -27,14 +26,14 @@ func (kdcjt *k8sDockerConfigJsonType) GenerateAuth() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DockerConfigJsonSecret struct {
|
type DockerConfigJsonSecret struct {
|
||||||
Name string
|
Name string
|
||||||
Uri string
|
Uri string
|
||||||
Username string
|
Username string
|
||||||
Password string
|
Password string
|
||||||
Email string
|
Email string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DockerConfigJsonSecret) ToYaml(encryptionKey *rsa.PublicKey) ([]byte, error) {
|
func (d *DockerConfigJsonSecret) ToYaml(encryptionKey string) ([]byte, error) {
|
||||||
k8sDockerConfigJson := k8sDockerConfigJsonType{
|
k8sDockerConfigJson := k8sDockerConfigJsonType{
|
||||||
Username: d.Username,
|
Username: d.Username,
|
||||||
Password: d.Password,
|
Password: d.Password,
|
||||||
@@ -51,8 +50,8 @@ func (d *DockerConfigJsonSecret) ToYaml(encryptionKey *rsa.PublicKey) ([]byte, e
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var k8sDockerConfigJsonMapMaybeEncryptedBytes []byte
|
var k8sDockerConfigJsonMapMaybeEncryptedBytes []byte
|
||||||
if encryptionKey != nil {
|
if encryptionKey != "" {
|
||||||
if k8sDockerConfigJsonMapMaybeEncryptedBytes, err = Encrypt(k8sDockerConfigJsonMapBytes, encryptionKey); err != nil {
|
if k8sDockerConfigJsonMapMaybeEncryptedBytes, err = EncryptData(k8sDockerConfigJsonMapBytes, encryptionKey); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -65,7 +64,7 @@ func (d *DockerConfigJsonSecret) ToYaml(encryptionKey *rsa.PublicKey) ([]byte, e
|
|||||||
Kind: "Secret",
|
Kind: "Secret",
|
||||||
},
|
},
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: d.Name,
|
Name: d.Name,
|
||||||
},
|
},
|
||||||
Type: v1.SecretTypeDockerConfigJson,
|
Type: v1.SecretTypeDockerConfigJson,
|
||||||
Data: map[string][]byte{
|
Data: map[string][]byte{
|
||||||
@@ -76,7 +75,7 @@ func (d *DockerConfigJsonSecret) ToYaml(encryptionKey *rsa.PublicKey) ([]byte, e
|
|||||||
return K8sSecretToYaml(k8sSecret)
|
return K8sSecretToYaml(k8sSecret)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DockerConfigJsonSecret) FromYaml(secretBytes []byte, decryptionKey *rsa.PrivateKey) error {
|
func (d *DockerConfigJsonSecret) FromYaml(secretBytes []byte, decryptionKey string) error {
|
||||||
k8sDockerConfigJsonMap := k8sDockerConfigJsonMapType{}
|
k8sDockerConfigJsonMap := k8sDockerConfigJsonMapType{}
|
||||||
if k8sSecret, err := K8sSecretFromYaml(secretBytes); err != nil {
|
if k8sSecret, err := K8sSecretFromYaml(secretBytes); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -86,7 +85,7 @@ func (d *DockerConfigJsonSecret) FromYaml(secretBytes []byte, decryptionKey *rsa
|
|||||||
return errors.New("not a kubernetes.io/dockerconfigjson type")
|
return errors.New("not a kubernetes.io/dockerconfigjson type")
|
||||||
} else if k8sDockerConfigJsonMapEncryptedBytes, ok := k8sSecret.Data[".dockerconfigjson"]; !ok {
|
} else if k8sDockerConfigJsonMapEncryptedBytes, ok := k8sSecret.Data[".dockerconfigjson"]; !ok {
|
||||||
return errors.New("secret data is missing a value for the .dockerconfigjson key")
|
return errors.New("secret data is missing a value for the .dockerconfigjson key")
|
||||||
} else if k8sDockerConfigJsonMapBytes, err := Decrypt(k8sDockerConfigJsonMapEncryptedBytes, decryptionKey); err != nil {
|
} else if k8sDockerConfigJsonMapBytes, err := DecryptData(k8sDockerConfigJsonMapEncryptedBytes, decryptionKey); err != nil {
|
||||||
return errors.New("secret data is missing a value for the .dockerconfigjson key")
|
return errors.New("secret data is missing a value for the .dockerconfigjson key")
|
||||||
} else if err := json.Unmarshal(k8sDockerConfigJsonMapBytes, &k8sDockerConfigJsonMap); err != nil {
|
} else if err := json.Unmarshal(k8sDockerConfigJsonMapBytes, &k8sDockerConfigJsonMap); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rsa"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -14,21 +12,21 @@ import (
|
|||||||
|
|
||||||
func TestDockerConfigJsonSecret(t *testing.T) {
|
func TestDockerConfigJsonSecret(t *testing.T) {
|
||||||
dockerConfigJsonSecret := DockerConfigJsonSecret{
|
dockerConfigJsonSecret := DockerConfigJsonSecret{
|
||||||
Name: "some-name",
|
Name: "some-name",
|
||||||
Uri: "some-uri",
|
Uri: "some-uri",
|
||||||
Username: "some-username",
|
Username: "some-username",
|
||||||
Password: "some-password",
|
Password: "some-password",
|
||||||
Email: "some-email",
|
Email: "some-email",
|
||||||
}
|
}
|
||||||
dockerConfigJsonSecretFromYaml := DockerConfigJsonSecret{}
|
dockerConfigJsonSecretFromYaml := DockerConfigJsonSecret{}
|
||||||
validYamlMap := map[string]interface{}{}
|
validYamlMap := map[string]interface{}{}
|
||||||
|
|
||||||
privateKey, err := rsa.GenerateKey(rand.Reader, RSA_KEY_LENGTH)
|
encryptionKey, err := GenerateKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error generating RSA private key: %v\n", err)
|
t.Fatalf("error generating RSA private key: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
dockerConfigJsonSecretYamlBytes, err := dockerConfigJsonSecret.ToYaml(&privateKey.PublicKey)
|
dockerConfigJsonSecretYamlBytes, err := dockerConfigJsonSecret.ToYaml(encryptionKey)
|
||||||
dockerConfigJsonMap := map[string]interface{}{}
|
dockerConfigJsonMap := map[string]interface{}{}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error converting secret to yaml: %v", err)
|
t.Fatalf("error converting secret to yaml: %v", err)
|
||||||
@@ -43,7 +41,7 @@ func TestDockerConfigJsonSecret(t *testing.T) {
|
|||||||
t.Fatalf("no .dockerconfigjson data key in the secret yaml: %v", string(dockerConfigJsonSecretYamlBytes))
|
t.Fatalf("no .dockerconfigjson data key in the secret yaml: %v", string(dockerConfigJsonSecretYamlBytes))
|
||||||
} else if dockerConfigJsonEncryptedBytes, err := base64.StdEncoding.DecodeString(dockerConfigJsonBytesBase64.(string)); err != nil {
|
} else if dockerConfigJsonEncryptedBytes, err := base64.StdEncoding.DecodeString(dockerConfigJsonBytesBase64.(string)); err != nil {
|
||||||
t.Fatalf("error decoding dockerConfigJsonBytes from base64: %v", err)
|
t.Fatalf("error decoding dockerConfigJsonBytes from base64: %v", err)
|
||||||
} else if dockerConfigJsonBytes, err := Decrypt(dockerConfigJsonEncryptedBytes, privateKey); err != nil {
|
} else if dockerConfigJsonBytes, err := DecryptData(dockerConfigJsonEncryptedBytes, encryptionKey); err != nil {
|
||||||
t.Fatalf("error decrypting dockerConfigJsonBytes: %v", err)
|
t.Fatalf("error decrypting dockerConfigJsonBytes: %v", err)
|
||||||
} else if err := json.Unmarshal(dockerConfigJsonBytes, &dockerConfigJsonMap); err != nil {
|
} else if err := json.Unmarshal(dockerConfigJsonBytes, &dockerConfigJsonMap); err != nil {
|
||||||
t.Fatalf("error unmarshalling dockerConfigJson from json: %v", err)
|
t.Fatalf("error unmarshalling dockerConfigJson from json: %v", err)
|
||||||
@@ -63,7 +61,7 @@ func TestDockerConfigJsonSecret(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.Logf("dockerConfigJsonSecretYaml: \n%v\n", string(dockerConfigJsonSecretYamlBytes))
|
t.Logf("dockerConfigJsonSecretYaml: \n%v\n", string(dockerConfigJsonSecretYamlBytes))
|
||||||
if err := dockerConfigJsonSecretFromYaml.FromYaml(dockerConfigJsonSecretYamlBytes, privateKey); err != nil {
|
if err := dockerConfigJsonSecretFromYaml.FromYaml(dockerConfigJsonSecretYamlBytes, encryptionKey); err != nil {
|
||||||
t.Fatalf("error reading secret in from yaml: %v", err)
|
t.Fatalf("error reading secret in from yaml: %v", err)
|
||||||
} else if !reflect.DeepEqual(dockerConfigJsonSecret, dockerConfigJsonSecretFromYaml) {
|
} else if !reflect.DeepEqual(dockerConfigJsonSecret, dockerConfigJsonSecretFromYaml) {
|
||||||
t.Fatalf("secret: %v does not equal secret: %v", dockerConfigJsonSecret, dockerConfigJsonSecretFromYaml)
|
t.Fatalf("secret: %v does not equal secret: %v", dockerConfigJsonSecret, dockerConfigJsonSecretFromYaml)
|
||||||
|
|||||||
@@ -1,58 +1,42 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/rsa"
|
"encoding/hex"
|
||||||
"crypto/x509"
|
"errors"
|
||||||
"encoding/pem"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
RSA_KEY_LENGTH = 4096
|
key_file_name = "user_secret_key"
|
||||||
|
|
||||||
QliksensePublicKey = "qliksensePub"
|
|
||||||
QliksensePrivateKey = "qliksensePriv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// GenerateAndStoreSecretKeypair generates and stores key pairs
|
// GenerateAndStoreSecretKey generates and stores key
|
||||||
func GenerateAndStoreSecretKeypair(secretsPath string) error {
|
func GenerateAndStoreSecretKey(secretsDir string) (string, error) {
|
||||||
LogDebugMessage("%s exists", secretsPath)
|
// creating contexts/qlik-default/secrets/user_secret_key
|
||||||
// creating contexts/qlik-default/secrets/qliksensePub and contexts/qlik-default/secrets/qliksensePriv files
|
keyFile := filepath.Join(secretsDir, key_file_name)
|
||||||
publicKeyFilePath := filepath.Join(secretsPath, QliksensePublicKey)
|
key, err := GenerateKey()
|
||||||
privateKeyFilePath := filepath.Join(secretsPath, QliksensePrivateKey)
|
|
||||||
LogDebugMessage("Generating public-private key pair.....")
|
|
||||||
GenerateRSAEncryptionKeys(publicKeyFilePath, privateKeyFilePath)
|
|
||||||
LogDebugMessage("Generated public-private key pairs")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenerateRSAEncryptionKeys is used to generate a new public-private key pair
|
|
||||||
func GenerateRSAEncryptionKeys(publicKeyFilePath, privateKeyFilePath string) error {
|
|
||||||
LogDebugMessage("Generating new RSA key pair")
|
|
||||||
privateKey, err := rsa.GenerateKey(rand.Reader, RSA_KEY_LENGTH)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error generating RSA private key: %v\n", err)
|
return "", err
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
if err := writeContentToFile([]byte(key), keyFile); err != nil {
|
||||||
privateKeyPEM := EncodePrivateKey(privateKey)
|
return "", err
|
||||||
if err := writeContentToFile(privateKeyPEM, privateKeyFilePath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
pubKeyPEM, err2 := EncodePublicKey(&privateKey.PublicKey)
|
return key, nil
|
||||||
if err2 != nil {
|
}
|
||||||
log.Printf("error occurred when encoding public key: %v\n", err2)
|
func LoadSecretKey(secretsDir string) (string, error) {
|
||||||
return err2
|
keyFile := filepath.Join(secretsDir, key_file_name)
|
||||||
|
by, err := ioutil.ReadFile(keyFile)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
if err := writeContentToFile(pubKeyPEM, publicKeyFilePath); err != nil {
|
return string(by), nil
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeContentToFile writes keys to a file
|
// writeContentToFile writes keys to a file
|
||||||
@@ -65,104 +49,54 @@ func writeContentToFile(keyData []byte, fileName string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt encrypts data with public key
|
func GenerateKey() (string, error) {
|
||||||
func Encrypt(pt []byte, pub *rsa.PublicKey) ([]byte, error) {
|
salt := make([]byte, 32)
|
||||||
//hash := sha512.New()
|
if _, err := rand.Read(salt); err != nil {
|
||||||
//ct, err := rsa.EncryptOAEP(hash, rand.Reader, pub, pt, nil)
|
return "", err
|
||||||
ct, err := rsa.EncryptPKCS1v15(rand.Reader, pub, pt)
|
}
|
||||||
|
s := fmt.Sprintf("%x", salt)
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func EncryptData(plaintext []byte, userKey string) ([]byte, error) {
|
||||||
|
key, _ := hex.DecodeString(userKey)
|
||||||
|
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return ct, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decrypt decrypts data with private key
|
aesgcm, err := cipher.NewGCM(block)
|
||||||
func Decrypt(ct []byte, priv *rsa.PrivateKey) ([]byte, error) {
|
|
||||||
// hash := sha512.New()
|
|
||||||
// plaintext, err := rsa.DecryptOAEP(hash, rand.Reader, priv, ciphertext, nil)
|
|
||||||
pt, err := rsa.DecryptPKCS1v15(rand.Reader, priv, ct)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return pt, nil
|
nonce := make([]byte, aesgcm.NonceSize())
|
||||||
|
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return aesgcm.Seal(nonce, nonce, plaintext, nil), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodePrivateKey private key to bytes
|
func DecryptData(ciphertext []byte, userKey string) ([]byte, error) {
|
||||||
func EncodePrivateKey(priv *rsa.PrivateKey) []byte {
|
key, _ := hex.DecodeString(userKey)
|
||||||
privBytes := pem.EncodeToMemory(
|
block, err := aes.NewCipher(key)
|
||||||
&pem.Block{
|
|
||||||
Type: "RSA PRIVATE KEY",
|
|
||||||
Bytes: x509.MarshalPKCS1PrivateKey(priv),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
return privBytes
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodePublicKey public key to bytes
|
|
||||||
func EncodePublicKey(pub *rsa.PublicKey) ([]byte, error) {
|
|
||||||
pubASN1, err := x509.MarshalPKIXPublicKey(pub)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pubBytes := pem.EncodeToMemory(&pem.Block{
|
aesgcm, err := cipher.NewGCM(block)
|
||||||
Type: "RSA PUBLIC KEY",
|
|
||||||
Bytes: pubASN1,
|
|
||||||
})
|
|
||||||
|
|
||||||
return pubBytes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeToPrivateKey bytes to private key
|
|
||||||
func DecodeToPrivateKey(priv []byte) (*rsa.PrivateKey, error) {
|
|
||||||
block, _ := pem.Decode(priv)
|
|
||||||
enc := x509.IsEncryptedPEMBlock(block)
|
|
||||||
b := block.Bytes
|
|
||||||
var err error
|
|
||||||
if enc {
|
|
||||||
log.Println("is encrypted pem block")
|
|
||||||
b, err = x509.DecryptPEMBlock(block, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
key, err := x509.ParsePKCS1PrivateKey(b)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return key, nil
|
nonceSize := aesgcm.NonceSize()
|
||||||
}
|
if len(ciphertext) < nonceSize {
|
||||||
|
return nil, errors.New("ciphertext too short")
|
||||||
// DecodeToPublicKey bytes to public key
|
|
||||||
func DecodeToPublicKey(pub []byte) (*rsa.PublicKey, error) {
|
|
||||||
block, _ := pem.Decode(pub)
|
|
||||||
enc := x509.IsEncryptedPEMBlock(block)
|
|
||||||
b := block.Bytes
|
|
||||||
var err error
|
|
||||||
if enc {
|
|
||||||
log.Println("is encrypted pem block")
|
|
||||||
b, err = x509.DecryptPEMBlock(block, nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
iface, err := x509.ParsePKIXPublicKey(b)
|
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
|
||||||
|
plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
key, ok := iface.(*rsa.PublicKey)
|
return plaintext, nil
|
||||||
if !ok {
|
|
||||||
err := fmt.Errorf("Unable to decode public key")
|
|
||||||
log.Println(err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return key, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,128 +1,29 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_generateRSAEncryptionKeys(t *testing.T) {
|
func Test_encrypt_decrypt(t *testing.T) {
|
||||||
tests := []struct {
|
key, err := GenerateKey()
|
||||||
name string
|
if err != nil {
|
||||||
wantErr bool
|
t.Log(err)
|
||||||
}{
|
t.FailNow()
|
||||||
{
|
|
||||||
name: "valid case",
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
testData := "this is a secret value"
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
enc, err := EncryptData([]byte(testData), key)
|
||||||
if err := GenerateAndStoreSecretKeypair(os.TempDir()); (err != nil) != tt.wantErr {
|
if err != nil {
|
||||||
t.Errorf("generateRSAEncryptionKeys() error = %v, wantErr %v", err, tt.wantErr)
|
t.Log(err)
|
||||||
}
|
t.FailNow()
|
||||||
})
|
}
|
||||||
}
|
dec, err := DecryptData(enc, key)
|
||||||
}
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
func Test_encryption_decryption(t *testing.T) {
|
t.FailNow()
|
||||||
privKeyBytes := []byte(`-----BEGIN RSA PRIVATE KEY-----
|
}
|
||||||
MIIJJwIBAAKCAgEAwUCimKCidbF3UxEHPy8K+hvhklRB9JYhj5sJy0if4lTVibkK
|
if testData != string(dec) {
|
||||||
1MrYCykOnmC40pPU9GLY1b8HxAg9tvyRn0YHUxOra6vVQaVcOVJhTM8D18d+lSr3
|
t.Log("expected: " + testData)
|
||||||
Lp1yiX+UGT4nzWI9+R1CCbwXrqeQVoZs6QZKynEXMkFI9/wNMOwPOvQFOSTuoEoC
|
t.Log("actual: " + string(dec))
|
||||||
O+zyTyUWEkNbUq825ELUQdIsjgmlWUOONudxsAr7ESRXW9QTHVh6uWmr3VRKZHby
|
t.Fail()
|
||||||
1JdU3I/wjdlGg5M2dDuXy5nQO9w/nYLjJXiw+zzOetZ/+t7/VOkOpNTeJQhwTM1W
|
|
||||||
F7Y2VLetbi9FHgyzHatrduh07+XEiTbgDf3GIx2bp2p6oh0G3N2zpiLcK/aZj8ro
|
|
||||||
uWWydfFfsU3MZ4FfJDP8I6b9awxjmKYqIr6hiPQCJaLBED8mwK+I5evIbnKv6E6u
|
|
||||||
K+BApWA/R7ElragoFYbqQ1VpvntVMtJt9Dy5ZrI+IQARdXD3bb34oh0IPBhClnvv
|
|
||||||
MUc1cWxDoXEX6oJ4I+LzxE87Zkwnan9qOwengolMVKFwPx1o37qrbmrXID21kKt7
|
|
||||||
FL6xN4HxHLkItr1fKzdyWDFRHgASTAWfx5BIwvPuUW0vZHkvO80VyV2L63whVhPn
|
|
||||||
PASmFkbviomrBttYfpr2aGQqF/qR1Nlxe834MFxk1pS9LMa/WnzvFr0gWakCAwEA
|
|
||||||
AQKCAgARSp9B2N2wejibDiL/3E23I1eDqFZedDB8kPrHXbAwqDaTJCN79spt9TaB
|
|
||||||
pVXkQaYEV/Pe7EDdoX8kKGU/QxzUqiXkdHOYdBtUZbKfFMbbP9ZrsnR7j0r4UpoF
|
|
||||||
yDH3hprU93E5PcNAtW2M0GpeT1nR01yn+n908PCdOAIE3GC7RDq1zOl2QzVLL55R
|
|
||||||
9ATv2Q2oTvJ/ETc7XlGVMx4+e2cIwXLFjeLjLI6pSYlxnarrGuetJZeEviWxto9n
|
|
||||||
odFVZI6yx8JFTXX8ZTCr/1IjwDDVyhMPmrHI2Lsv9cqBpSpbVe32cUkKxhsGaYjz
|
|
||||||
GvesQKamOPhco2ATNxPm0yopFlPsGKMfVl0BK0J6BqFh1BvU/SYJmXfnFuUNO3vV
|
|
||||||
4u2Saa0q1iddxV0rXDwIqUfn+S6rwzK0G7y8bH2yvpB2VwiG3TFPnULep4wsefNq
|
|
||||||
Fj92kqFBjacGpQLEEslUY0CMgeZ2+NuBQSUTscP3wBRsottMR6YXJtINdvfHBx+e
|
|
||||||
EcN71z8D00w3mYqIQ7qb4Ml6HOqknunn58g43L9sACMUMTlEBXa9pUnScNYgWBAz
|
|
||||||
W2q2mH37cIydM2JRZPpA8B4yTHt5ugJmChwyNFM7941arjKrebH+6AzLkofGedOP
|
|
||||||
zg+vZQuPEXWs+3MBBnkWoyJW3Y0fbQdjsuQTtnd+7iyoxoBroQKCAQEA4dIiFlIS
|
|
||||||
MDfRhQQWSiDvaw9aneDEJ3uo63ZRH5tm/IynLgtjYgEm/ZxlBCQgqRKLYELBxhu8
|
|
||||||
SaF0uPK8pmpFJt0mIwSlsdeVhuE2obQeKUCczaqrKeaHS3PdWLjTlwph81BGRkHy
|
|
||||||
qfqtNylyyMxrdEbnR51EtsWgFq6anTUAui1Q09JMuMNZRMOzDs1F4gExgD22rc0V
|
|
||||||
c9YQ+jHJRxBGtNKMpMEqc8cvaxBidbItrN9SMTSWog7uYPBuEuaJ6K9vpgyJMOzJ
|
|
||||||
SYcQEFGqgIqIDCg+ABE4d/4YROMKZ1DV/bJCind9brUHSx6XALsF0nC5c1Q9TnUL
|
|
||||||
qI2khOwts4KYKwKCAQEA2xRC6Az97Vkdzu7BjLJ1FKmx4S2nEEgVS12ds82U+5Xf
|
|
||||||
BHKAJnjqlqmmpzzJG+d77IYktz0+mey1QCNkqlm2fhuKs8LZMnpZRf0l8VcoBsUP
|
|
||||||
/xKz7wfiE7RRFZtLJhPp4hhe43GzX5/JFMWMnC6UykwQbj4t1E/GNM/Suqwvg12M
|
|
||||||
wktAJ6nqLgfhjQSO4xWo+nPzcbX+fNtrPCZVrBhYXihhcwRRNImWUCGJ6J4LMdPY
|
|
||||||
Y9Z59qhOvE9cReH/Xw1av46omyiSyAqlgPyZ/kzA2IJSqYCjiQR/2+RD/g13jpcJ
|
|
||||||
jatXLVZ8MJSL5OTS40G/HHTNNpNHbKKh0GOyxBA3ewKCAQBAn8UXhCcmW2L/YPsL
|
|
||||||
/b7mcX9qPP+FmRLvR23R0MQ5M/tH5wRq8I969n3GIJykJeVzB8eybQ+GNslTgEvS
|
|
||||||
iAkAJTubu+G7MkndTqg2wHf9MDtvdA8Fr646Po8yq7oJuHPtkKR7yLWsRUu6xIbP
|
|
||||||
xgheP0hCq1QVxhqZQyCGKrvpi7xc0gsYuPbcAfFFJCOCmPrUi1SzCkTAYJt9LjA+
|
|
||||||
wP6rErIjGBCRD4iXaBn1OqdtmH9KC5WsDP/VCBlIGWeQCly2NVIxiSHVg+xp7yUP
|
|
||||||
IhXq/L05gbQaSsIhPKQmivCiaJg4The8TdwneDqYf+0bmxzHT203/bD3bImPbJNr
|
|
||||||
ksz/AoIBAEwu4Y1cZzkQUmNRd5D7xecnk6ngfEYXKwCIT3zlMrfCSEl9n77BMaKu
|
|
||||||
4Dsr0iuX9eosQ7xM2eYhAG6LYEg05lc4MKWOToVVMpI6E+W3Dz47bPKgiF3I+f8s
|
|
||||||
Jz5CQIG/TwfGvciOE3hfUkec4ua09BzdEqGjkcBQ9XYMBxXPJr6h2379OBQS7FKR
|
|
||||||
fwfQ2/dv4tElXTTfut2kV8gU9Jnh5Wjo1epvR+XjKpg28YQo4W+0YX1magcyRB8L
|
|
||||||
4eSTUIC3XiVa8Jr0IwbZXPBb5xkdi7o+p4w2JahSHjxTRqmj+T1mnHXdbXVgq9Mg
|
|
||||||
9Pzl7cgFZvX4UBx4XtASRf73jITNtt0CggEADH9K+O7FrIOSQly0sMvsRCMtejp3
|
|
||||||
o+MDh1Q+vEg2kEgNXjS4ZFVljUpM2kg1OdUz7feS4dLXUJiIQ8ZWtZPedcq7wjHd
|
|
||||||
02he5+s06l0jPifN3tX1ADfXGpXg5R2fbkrIzakkPP5/RO/aDxIUo7qhklNsVTXO
|
|
||||||
VlGGfWLdk0ekA4upKm02Q1+YOlbIcAicEYYY8K7IffUwnohzKwL9yfuGi1VKTXpE
|
|
||||||
4fzdegsHI03FSqR7V+LvtBpIupQ7RO4kuBmCEyI4E9FVknchg4te4gO3qwd9y0rJ
|
|
||||||
Gu7HNIOrwOHzviI7J6Nd/l9MmeKqklHSgJvko/f5TmiXuQQ8xDZf84rcjQ==
|
|
||||||
-----END RSA PRIVATE KEY-----
|
|
||||||
`)
|
|
||||||
|
|
||||||
publicKeyBytes := []byte(`-----BEGIN RSA PUBLIC KEY-----
|
|
||||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwUCimKCidbF3UxEHPy8K
|
|
||||||
+hvhklRB9JYhj5sJy0if4lTVibkK1MrYCykOnmC40pPU9GLY1b8HxAg9tvyRn0YH
|
|
||||||
UxOra6vVQaVcOVJhTM8D18d+lSr3Lp1yiX+UGT4nzWI9+R1CCbwXrqeQVoZs6QZK
|
|
||||||
ynEXMkFI9/wNMOwPOvQFOSTuoEoCO+zyTyUWEkNbUq825ELUQdIsjgmlWUOONudx
|
|
||||||
sAr7ESRXW9QTHVh6uWmr3VRKZHby1JdU3I/wjdlGg5M2dDuXy5nQO9w/nYLjJXiw
|
|
||||||
+zzOetZ/+t7/VOkOpNTeJQhwTM1WF7Y2VLetbi9FHgyzHatrduh07+XEiTbgDf3G
|
|
||||||
Ix2bp2p6oh0G3N2zpiLcK/aZj8rouWWydfFfsU3MZ4FfJDP8I6b9awxjmKYqIr6h
|
|
||||||
iPQCJaLBED8mwK+I5evIbnKv6E6uK+BApWA/R7ElragoFYbqQ1VpvntVMtJt9Dy5
|
|
||||||
ZrI+IQARdXD3bb34oh0IPBhClnvvMUc1cWxDoXEX6oJ4I+LzxE87Zkwnan9qOwen
|
|
||||||
golMVKFwPx1o37qrbmrXID21kKt7FL6xN4HxHLkItr1fKzdyWDFRHgASTAWfx5BI
|
|
||||||
wvPuUW0vZHkvO80VyV2L63whVhPnPASmFkbviomrBttYfpr2aGQqF/qR1Nlxe834
|
|
||||||
MFxk1pS9LMa/WnzvFr0gWakCAwEAAQ==
|
|
||||||
-----END RSA PUBLIC KEY-----
|
|
||||||
`)
|
|
||||||
origStr := "Value1234"
|
|
||||||
|
|
||||||
pubKey, err := DecodeToPublicKey(publicKeyBytes)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
privKey, err := DecodeToPrivateKey(privKeyBytes)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
encData, err := Encrypt([]byte(origStr), pubKey)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
encDataStr := base64.StdEncoding.EncodeToString(encData)
|
|
||||||
log.Println("Encoded text:", encDataStr)
|
|
||||||
dec, _ := base64.StdEncoding.DecodeString(encDataStr)
|
|
||||||
data, err := Decrypt(dec, privKey)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
if string(data) != origStr {
|
|
||||||
t.Error("original string and decrypted string don't match")
|
|
||||||
t.FailNow()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,20 +98,21 @@ func kubectlOperation(manifests string, oprName string, namespace string) error
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func KubectlDirectOps(opr []string, namespace string) error {
|
func KubectlDirectOps(opr []string, namespace string) (string, error) {
|
||||||
arguments := []string{}
|
arguments := []string{}
|
||||||
if namespace != "" {
|
if namespace != "" {
|
||||||
arguments = append(arguments, "-n", namespace)
|
arguments = append(arguments, "-n", namespace)
|
||||||
}
|
}
|
||||||
arguments = append(arguments, opr...)
|
arguments = append(arguments, opr...)
|
||||||
|
var out bytes.Buffer
|
||||||
cmd := exec.Command("kubectl", arguments...)
|
cmd := exec.Command("kubectl", arguments...)
|
||||||
LogDebugMessage("Kubectl command: %s %v\n", "kubectl", arguments)
|
LogDebugMessage("Kubectl command: %s %v\n", "kubectl", arguments)
|
||||||
sterrBuffer := &bytes.Buffer{}
|
sterrBuffer := &bytes.Buffer{}
|
||||||
cmd.Stderr = sterrBuffer
|
cmd.Stderr = sterrBuffer
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = &out
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
return fmt.Errorf("kubectl %v failed with: %v, %v\n", opr, err, sterrBuffer.String())
|
return "", fmt.Errorf("kubectl %v failed with: %v, %v\n", opr, err, sterrBuffer.String())
|
||||||
}
|
}
|
||||||
return nil
|
s := out.String()
|
||||||
|
return s, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func TestKubectlDirectOps(t *testing.T) {
|
|||||||
ns := GetKubectlNamespace()
|
ns := GetKubectlNamespace()
|
||||||
opr := fmt.Sprintf("version")
|
opr := fmt.Sprintf("version")
|
||||||
opr1 := strings.Fields(opr)
|
opr1 := strings.Fields(opr)
|
||||||
err := KubectlDirectOps(opr1, ns)
|
_, err := KubectlDirectOps(opr1, ns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Log(err)
|
t.Log(err)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
|
|||||||
124
pkg/api/preflight_apis.go
Normal file
124
pkg/api/preflight_apis.go
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PreflightConfig struct {
|
||||||
|
metav1.TypeMeta `json:",inline" yaml:",inline"`
|
||||||
|
metav1.ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||||
|
Spec *PreflightSpec `json:"spec" yaml:"spec"`
|
||||||
|
QliksenseHomePath string `json:"-" yaml:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PreflightSpec struct {
|
||||||
|
MinK8sVersion string `json:"minK8sVersion,omitempty" yaml:"minK8sVersion,omitempty"`
|
||||||
|
Images map[string]string `json:"images,omitempty" yaml:"images,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewPreflightConfigEmpty create empty PreflightConfig object
|
||||||
|
func NewPreflightConfigEmpty(qHome string) *PreflightConfig {
|
||||||
|
p := &PreflightConfig{
|
||||||
|
QliksenseHomePath: qHome,
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: "config.qlik.com/v1",
|
||||||
|
Kind: "PreflightConfig",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "PreflightConfigMetadata",
|
||||||
|
},
|
||||||
|
Spec: &PreflightSpec{},
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewPreflightConfig create empty PreflightConfig object if preflit/preflight-config.yaml not exist
|
||||||
|
func NewPreflightConfig(qHome string) *PreflightConfig {
|
||||||
|
p := NewPreflightConfigEmpty(qHome)
|
||||||
|
conFile := p.GetConfigFilePath()
|
||||||
|
if _, err := os.Lstat(conFile); err != nil {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
p = &PreflightConfig{
|
||||||
|
QliksenseHomePath: qHome,
|
||||||
|
}
|
||||||
|
if err := ReadFromFile(p, conFile); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
//GetConfigFilePath return preflight-config.yaml file path
|
||||||
|
func (p *PreflightConfig) GetConfigFilePath() string {
|
||||||
|
return filepath.Join(p.QliksenseHomePath, "preflight", "preflight-config.yaml")
|
||||||
|
}
|
||||||
|
|
||||||
|
//Write write PreflightConfig object into the ~/.qliksense/preflight/preflight-config.yaml file
|
||||||
|
func (p *PreflightConfig) Write() error {
|
||||||
|
pDir := filepath.Join(p.QliksenseHomePath, "preflight")
|
||||||
|
if err := os.MkdirAll(pDir, os.ModePerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return WriteToFile(p, p.GetConfigFilePath())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PreflightConfig) AddMinK8sV(version string) {
|
||||||
|
if p.Spec == nil {
|
||||||
|
p.Spec = &PreflightSpec{}
|
||||||
|
}
|
||||||
|
p.Spec.MinK8sVersion = version
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PreflightConfig) AddImage(imageFor, imageName string) {
|
||||||
|
if p.Spec.Images == nil {
|
||||||
|
p.Spec.Images = make(map[string]string)
|
||||||
|
}
|
||||||
|
p.Spec.Images[imageFor] = imageName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PreflightConfig) GetImageName(imageFor string, accountForImageRegistry bool) (string, error) {
|
||||||
|
if p.Spec.Images == nil {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
image := p.Spec.Images[imageFor]
|
||||||
|
if accountForImageRegistry {
|
||||||
|
qConfig := NewQConfig(p.QliksenseHomePath)
|
||||||
|
if currentCR, err := qConfig.GetCurrentCR(); err != nil {
|
||||||
|
return "", err
|
||||||
|
} else if imageRegistry := currentCR.Spec.GetImageRegistry(); imageRegistry != "" {
|
||||||
|
imageSegments := strings.Split(image, "/")
|
||||||
|
imageNameAndTag := imageSegments[len(imageSegments)-1]
|
||||||
|
return path.Join(imageRegistry, imageNameAndTag), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return image, nil
|
||||||
|
}
|
||||||
|
func (p *PreflightConfig) GetMinK8sVersion() string {
|
||||||
|
return p.Spec.MinK8sVersion
|
||||||
|
}
|
||||||
|
func (p *PreflightConfig) IsExistOnDisk() bool {
|
||||||
|
if _, err := os.Lstat(p.GetConfigFilePath()); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PreflightConfig) GetImageMap() map[string]string {
|
||||||
|
return p.Spec.Images
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PreflightConfig) Initialize() error {
|
||||||
|
if p.IsExistOnDisk() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
p.AddMinK8sV("1.15")
|
||||||
|
p.AddImage("nginx", "nginx")
|
||||||
|
p.AddImage("netcat", "subfuzion/netcat")
|
||||||
|
p.AddImage("mongo", "mongo")
|
||||||
|
return p.Write()
|
||||||
|
}
|
||||||
138
pkg/api/preflight_apis_test.go
Normal file
138
pkg/api/preflight_apis_test.go
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_Initalize(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
validate func(t *testing.T, tempDir string)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "without account for imageRegistry",
|
||||||
|
validate: func(t *testing.T, tempDir string) {
|
||||||
|
preflightConfig := NewPreflightConfig(tempDir)
|
||||||
|
imageName, err := preflightConfig.GetImageName("test", false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if imageName != "testimage" {
|
||||||
|
t.Fatalf("expected image name: testimage, got: %v", imageName)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with account for configured imageRegistry",
|
||||||
|
validate: func(t *testing.T, tempDir string) {
|
||||||
|
registry := "registryFoo"
|
||||||
|
setupQliksenseTestDefaultContext(t, tempDir, fmt.Sprintf(`
|
||||||
|
apiVersion: qlik.com/v1
|
||||||
|
kind: Qliksense
|
||||||
|
metadata:
|
||||||
|
name: qlik-default
|
||||||
|
spec:
|
||||||
|
configs:
|
||||||
|
qliksense:
|
||||||
|
- name: imageRegistry
|
||||||
|
value: %v
|
||||||
|
`, registry))
|
||||||
|
preflightConfig := NewPreflightConfig(tempDir)
|
||||||
|
imageName, err := preflightConfig.GetImageName("test", true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
expectedImageName := fmt.Sprintf("%v/testimage", registry)
|
||||||
|
if imageName != expectedImageName {
|
||||||
|
t.Fatalf("expected image name: %v, got: %v", expectedImageName, imageName)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "with account for un-configured imageRegistry",
|
||||||
|
validate: func(t *testing.T, tempDir string) {
|
||||||
|
setupQliksenseTestDefaultContext(t, tempDir, `
|
||||||
|
apiVersion: qlik.com/v1
|
||||||
|
kind: Qliksense
|
||||||
|
metadata:
|
||||||
|
name: qlik-default
|
||||||
|
spec:
|
||||||
|
configs:
|
||||||
|
qliksense:
|
||||||
|
- name: something
|
||||||
|
value: other
|
||||||
|
`)
|
||||||
|
preflightConfig := NewPreflightConfig(tempDir)
|
||||||
|
imageName, err := preflightConfig.GetImageName("test", true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
expectedImageName := "testimage"
|
||||||
|
if imageName != expectedImageName {
|
||||||
|
t.Fatalf("expected image name: %v, got: %v", expectedImageName, imageName)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
tempDir, err := ioutil.TempDir("", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tempDir)
|
||||||
|
setupPreflightConfig(t, tempDir)
|
||||||
|
testCase.validate(t, tempDir)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupPreflightConfig(t *testing.T, tempDir string) {
|
||||||
|
pf := NewPreflightConfig(tempDir)
|
||||||
|
if err := pf.Initialize(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
p := &PreflightConfig{
|
||||||
|
QliksenseHomePath: tempDir,
|
||||||
|
}
|
||||||
|
if err := ReadFromFile(p, pf.GetConfigFilePath()); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if p.GetMinK8sVersion() != "1.15" {
|
||||||
|
t.Fatalf("expected k8 version: 1.15, but got " + p.GetMinK8sVersion())
|
||||||
|
}
|
||||||
|
p.AddImage("test", "testimage")
|
||||||
|
if err := p.Write(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupQliksenseTestDefaultContext(t *testing.T, tmpQlikSenseHome, CR string) {
|
||||||
|
if err := ioutil.WriteFile(path.Join(tmpQlikSenseHome, "config.yaml"), []byte(`
|
||||||
|
apiVersion: config.qlik.com/v1
|
||||||
|
kind: QliksenseConfig
|
||||||
|
metadata:
|
||||||
|
name: QliksenseConfigMetadata
|
||||||
|
spec:
|
||||||
|
contexts:
|
||||||
|
- name: qlik-default
|
||||||
|
crFile: contexts/qlik-default/qlik-default.yaml
|
||||||
|
currentContext: qlik-default
|
||||||
|
`), os.ModePerm); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultContextDir := path.Join(tmpQlikSenseHome, "contexts", "qlik-default")
|
||||||
|
if err := os.MkdirAll(defaultContextDir, os.ModePerm); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ioutil.WriteFile(path.Join(defaultContextDir, "qlik-default.yaml"), []byte(CR), os.ModePerm); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"archive/tar"
|
"archive/tar"
|
||||||
"archive/zip"
|
"archive/zip"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
|
b64 "encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@@ -11,7 +12,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -62,27 +62,38 @@ func ReadKeys(keyFile string) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ProcessConfigArgs processes args and returns an service, key, value slice
|
// ProcessConfigArgs processes args and returns an service, key, value slice
|
||||||
func ProcessConfigArgs(args []string) ([]*ServiceKeyValue, error) {
|
func ProcessConfigArgs(args []string, base64Encoded bool) ([]*ServiceKeyValue, error) {
|
||||||
// prepare received args
|
// prepare received args
|
||||||
// split args[0] into key and value
|
// split args[0] into key and value
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
err := fmt.Errorf("No args were provided. Please provide args to configure the current context")
|
err := fmt.Errorf("No args were provided. Please provide args to configure the current context")
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
notValidErr := fmt.Errorf("Please provide valid args for this command")
|
||||||
resultSvcKV := make([]*ServiceKeyValue, len(args))
|
resultSvcKV := make([]*ServiceKeyValue, len(args))
|
||||||
re1 := regexp.MustCompile(`(\w{1,}).(\w{1,})=("*[\w\-?=_/:0-9]+"*)`)
|
// qliksense.mongodb=somethig
|
||||||
for i, arg := range args {
|
for i, arg := range args {
|
||||||
LogDebugMessage("Arg received: %s", arg)
|
LogDebugMessage("Arg received: %s", arg)
|
||||||
result := re1.FindStringSubmatch(arg)
|
first := strings.SplitN(arg, "=", 2)
|
||||||
// check if result array's length is == 4 (index 0 - is the full match & indices 1,2,3- are the fields we need)
|
if len(first) != 2 {
|
||||||
if len(result) != 4 {
|
return nil, notValidErr
|
||||||
err := fmt.Errorf("Please provide valid args for this command")
|
}
|
||||||
return nil, err
|
second := strings.SplitN(first[0], ".", 2)
|
||||||
|
if len(second) != 2 {
|
||||||
|
return nil, notValidErr
|
||||||
|
}
|
||||||
|
resultValue := strings.Trim(first[1], "\"")
|
||||||
|
if base64Encoded {
|
||||||
|
if decodeByte, err := b64.StdEncoding.DecodeString(resultValue); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
resultValue = strings.Trim(string(decodeByte), "\n ")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
resultSvcKV[i] = &ServiceKeyValue{
|
resultSvcKV[i] = &ServiceKeyValue{
|
||||||
SvcName: result[1],
|
SvcName: second[0],
|
||||||
Key: result[2],
|
Key: second[1],
|
||||||
Value: strings.ReplaceAll(result[3], `"`, ""),
|
Value: resultValue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return resultSvcKV, nil
|
return resultSvcKV, nil
|
||||||
|
|||||||
47
pkg/api/utils_test.go
Normal file
47
pkg/api/utils_test.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestProcessConfigArgs(t *testing.T) {
|
||||||
|
args := []string{
|
||||||
|
"qliksense.mongodb=mongouri://something?ffall",
|
||||||
|
"test_under.test=value_under",
|
||||||
|
"test-dash.dash-key=value-dash",
|
||||||
|
"test-dot.dot-key=127.0.0.1",
|
||||||
|
"test123.key123=value123",
|
||||||
|
"test-equal.keyequal=newvalue=@hj",
|
||||||
|
}
|
||||||
|
expectedKeys := []string{"mongodb", "test", "dash-key", "dot-key", "key123", "keyequal"}
|
||||||
|
expectedValue := []string{"mongouri://something?ffall", "value_under", "value-dash", "127.0.0.1", "value123", "newvalue=@hj"}
|
||||||
|
exppectedSvc := []string{"qliksense", "test_under", "test-dash", "test-dot", "test123", "test-equal"}
|
||||||
|
sv, err := ProcessConfigArgs(args, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Log(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
for _, v := range sv {
|
||||||
|
if !contains(expectedKeys, v.Key) {
|
||||||
|
t.Fail()
|
||||||
|
t.Log("expectd key " + v.Key + " not found")
|
||||||
|
}
|
||||||
|
if !contains(expectedValue, v.Value) {
|
||||||
|
t.Fail()
|
||||||
|
t.Log("expectd Value " + v.Value + " not found")
|
||||||
|
}
|
||||||
|
if !contains(exppectedSvc, v.SvcName) {
|
||||||
|
t.Fail()
|
||||||
|
t.Log("expectd service " + v.SvcName + " not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func contains(arr []string, str string) bool {
|
||||||
|
for _, a := range arr {
|
||||||
|
if a == str {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -4,13 +4,104 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (qp *QliksensePreflight) RunAllPreflightChecks() error {
|
func (qp *QliksensePreflight) RunAllPreflightChecks(namespace string, kubeConfigContents []byte, mongodbUrl string) {
|
||||||
//run all preflight checks
|
|
||||||
fmt.Println("Running all preflight checks")
|
checkCount := 0
|
||||||
fmt.Printf("\nRunning DNS check...\n")
|
totalCount := 0
|
||||||
qp.CheckDns()
|
// Preflight minimum kuberenetes version check
|
||||||
fmt.Printf("\nRunning minimum kubernetes version check...\n")
|
fmt.Printf("\nPreflight kubernetes minimum version check\n")
|
||||||
qp.CheckK8sVersion()
|
fmt.Println("------------------------------------------")
|
||||||
|
if err := qp.CheckK8sVersion(namespace, kubeConfigContents); err != nil {
|
||||||
|
fmt.Printf("Preflight kubernetes minimum version check: FAILED\n")
|
||||||
|
} else {
|
||||||
|
checkCount++
|
||||||
|
}
|
||||||
|
totalCount++
|
||||||
|
|
||||||
|
// Preflight deployment check
|
||||||
|
fmt.Printf("\nPreflight deployment check\n")
|
||||||
|
fmt.Println("--------------------------")
|
||||||
|
if err := qp.CheckDeployment(namespace, kubeConfigContents); err != nil {
|
||||||
|
fmt.Printf("Preflight deployment check: FAILED\n")
|
||||||
|
} else {
|
||||||
|
checkCount++
|
||||||
|
}
|
||||||
|
totalCount++
|
||||||
|
|
||||||
|
// Preflight service check
|
||||||
|
fmt.Printf("\nPreflight service check\n")
|
||||||
|
fmt.Println("-----------------------")
|
||||||
|
if err := qp.CheckService(namespace, kubeConfigContents); err != nil {
|
||||||
|
fmt.Printf("Preflight service check: FAILED\n")
|
||||||
|
} else {
|
||||||
|
checkCount++
|
||||||
|
}
|
||||||
|
totalCount++
|
||||||
|
|
||||||
|
// Preflight pod check
|
||||||
|
fmt.Printf("\nPreflight pod check\n")
|
||||||
|
fmt.Println("-----------------------")
|
||||||
|
if err := qp.CheckPod(namespace, kubeConfigContents); err != nil {
|
||||||
|
fmt.Printf("Preflight pod check: FAILED\n")
|
||||||
|
} else {
|
||||||
|
checkCount++
|
||||||
|
}
|
||||||
|
totalCount++
|
||||||
|
|
||||||
|
// Preflight role check
|
||||||
|
fmt.Printf("\nPreflight role check\n")
|
||||||
|
fmt.Println("--------------------------")
|
||||||
|
if err := qp.CheckCreateRole(namespace); err != nil {
|
||||||
|
fmt.Printf("Preflight role check: FAILED\n")
|
||||||
|
} else {
|
||||||
|
checkCount++
|
||||||
|
}
|
||||||
|
totalCount++
|
||||||
|
|
||||||
|
// Preflight rolebinding check
|
||||||
|
fmt.Printf("\nPreflight rolebinding check\n")
|
||||||
|
fmt.Println("---------------------------------")
|
||||||
|
if err := qp.CheckCreateRoleBinding(namespace); err != nil {
|
||||||
|
fmt.Printf("Preflight rolebinding check: FAILED\n")
|
||||||
|
} else {
|
||||||
|
checkCount++
|
||||||
|
}
|
||||||
|
totalCount++
|
||||||
|
|
||||||
|
// Preflight serviceaccount check
|
||||||
|
fmt.Printf("\nPreflight serviceaccount check\n")
|
||||||
|
fmt.Println("------------------------------------")
|
||||||
|
if err := qp.CheckCreateServiceAccount(namespace); err != nil {
|
||||||
|
fmt.Printf("Preflight serviceaccount check: FAILED\n")
|
||||||
|
} else {
|
||||||
|
checkCount++
|
||||||
|
}
|
||||||
|
totalCount++
|
||||||
|
|
||||||
|
// Preflight mongo check
|
||||||
|
fmt.Printf("\nPreflight mongo check\n")
|
||||||
|
fmt.Println("---------------------")
|
||||||
|
if err := qp.CheckMongo(kubeConfigContents, namespace, mongodbUrl); err != nil {
|
||||||
|
fmt.Printf("Preflight mongo check: FAILED\n")
|
||||||
|
} else {
|
||||||
|
checkCount++
|
||||||
|
}
|
||||||
|
totalCount++
|
||||||
|
|
||||||
|
// Preflight DNS check
|
||||||
|
fmt.Printf("\nPreflight DNS check\n")
|
||||||
|
fmt.Println("-------------------")
|
||||||
|
if err := qp.CheckDns(namespace, kubeConfigContents); err != nil {
|
||||||
|
fmt.Printf("Preflight DNS check: FAILED\n")
|
||||||
|
} else {
|
||||||
|
checkCount++
|
||||||
|
}
|
||||||
|
totalCount++
|
||||||
|
|
||||||
|
if checkCount == totalCount {
|
||||||
|
fmt.Printf("\nAll preflight checks have PASSED\n")
|
||||||
|
} else {
|
||||||
|
fmt.Printf("\n1 or more preflight checks have FAILED\n")
|
||||||
|
}
|
||||||
fmt.Println("Completed running all preflight checks")
|
fmt.Println("Completed running all preflight checks")
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
128
pkg/preflight/deployability.go
Normal file
128
pkg/preflight/deployability.go
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package preflight
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (qp *QliksensePreflight) CheckDeployment(namespace string, kubeConfigContents []byte) error {
|
||||||
|
clientset, _, err := getK8SClientSet(kubeConfigContents, "")
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Kube config error: %v\n", err)
|
||||||
|
fmt.Print(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deployment check
|
||||||
|
fmt.Printf("Preflight deployment check: \n")
|
||||||
|
err = qp.checkPfDeployment(clientset, namespace, "deployment-preflight-check")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Preflight Deployment check: FAILED")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println("Completed preflight deployment check")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qp *QliksensePreflight) CheckService(namespace string, kubeConfigContents []byte) error {
|
||||||
|
clientset, _, err := getK8SClientSet(kubeConfigContents, "")
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error: unable to create a kubernetes client: %v\n", err)
|
||||||
|
fmt.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Service check
|
||||||
|
fmt.Printf("\nPreflight service check: \n")
|
||||||
|
err = checkPfService(clientset, namespace)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Preflight Service check: FAILED")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println("Completed preflight service check")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qp *QliksensePreflight) CheckPod(namespace string, kubeConfigContents []byte) error {
|
||||||
|
clientset, _, err := getK8SClientSet(kubeConfigContents, "")
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error: unable to create a kubernetes client: %v\n", err)
|
||||||
|
fmt.Print(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Pod check
|
||||||
|
fmt.Printf("\nPreflight pod check: \n")
|
||||||
|
|
||||||
|
err = qp.checkPfPod(clientset, namespace)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Preflight Pod check: FAILED")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println("Completed preflight pod check")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qp *QliksensePreflight) checkPfPod(clientset *kubernetes.Clientset, namespace string) error {
|
||||||
|
// create a pod
|
||||||
|
podName := "pod-pf-check"
|
||||||
|
commandToRun := []string{}
|
||||||
|
|
||||||
|
imageName, err := qp.GetPreflightConfigObj().GetImageName(nginx, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pod, err := createPreflightTestPod(clientset, namespace, podName, imageName, commandToRun)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error: unable to create pod %s - %v\n", podName, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer deletePod(clientset, namespace, podName)
|
||||||
|
|
||||||
|
if err := waitForPod(clientset, namespace, pod); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Preflight pod creation check: PASSED")
|
||||||
|
fmt.Println("Cleaning up resources...")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkPfService(clientset *kubernetes.Clientset, namespace string) error {
|
||||||
|
// creating service
|
||||||
|
serviceName := "svc-pf-check"
|
||||||
|
pfService, err := createPreflightTestService(clientset, namespace, serviceName)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error: unable to create service : %s\n", serviceName)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer deleteService(clientset, namespace, serviceName)
|
||||||
|
_, err = getService(clientset, namespace, pfService.GetName())
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error: unable to retrieve service: %s\n", serviceName)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println("Preflight service creation check: PASSED")
|
||||||
|
fmt.Println("Cleaning up resources...")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qp *QliksensePreflight) checkPfDeployment(clientset *kubernetes.Clientset, namespace, depName string) error {
|
||||||
|
// check if we are able to create a deployment
|
||||||
|
imageName, err := qp.GetPreflightConfigObj().GetImageName(nginx, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pfDeployment, err := createPreflightTestDeployment(clientset, namespace, depName, imageName)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error: unable to create deployment: %v\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer deleteDeployment(clientset, namespace, depName)
|
||||||
|
if err := waitForDeployment(clientset, namespace, pfDeployment); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println("Preflight Deployment check: PASSED")
|
||||||
|
fmt.Println("Cleaning up resources...")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,128 +1,92 @@
|
|||||||
package preflight
|
package preflight
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"strings"
|
||||||
"io/ioutil"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/qlik-oss/sense-installer/pkg/api"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const dnsCheckYAML = `
|
const (
|
||||||
apiVersion: troubleshoot.replicated.com/v1beta1
|
nginx = "nginx"
|
||||||
kind: Preflight
|
netcat = "netcat"
|
||||||
metadata:
|
)
|
||||||
name: cluster-preflight-checks
|
|
||||||
namespace: {{ . }}
|
|
||||||
spec:
|
|
||||||
collectors:
|
|
||||||
- run:
|
|
||||||
collectorName: spin-up-pod
|
|
||||||
args: ["-z", "-v", "-w 1", "qnginx001", "80"]
|
|
||||||
command: ["nc"]
|
|
||||||
image: subfuzion/netcat:latest
|
|
||||||
imagePullPolicy: IfNotPresent
|
|
||||||
name: spin-up-pod-check-dns
|
|
||||||
namespace: {{ . }}
|
|
||||||
timeout: 30s
|
|
||||||
|
|
||||||
analyzers:
|
func (qp *QliksensePreflight) CheckDns(namespace string, kubeConfigContents []byte) error {
|
||||||
- textAnalyze:
|
clientset, _, err := getK8SClientSet(kubeConfigContents, "")
|
||||||
checkName: DNS check
|
|
||||||
collectorName: spin-up-pod-check-dns
|
|
||||||
fileName: spin-up-pod.txt
|
|
||||||
regex: succeeded
|
|
||||||
outcomes:
|
|
||||||
- fail:
|
|
||||||
message: DNS check failed
|
|
||||||
- pass:
|
|
||||||
message: DNS check passed
|
|
||||||
`
|
|
||||||
|
|
||||||
func (qp *QliksensePreflight) CheckDns() error {
|
|
||||||
// retrieve namespace
|
|
||||||
namespace := api.GetKubectlNamespace()
|
|
||||||
|
|
||||||
api.LogDebugMessage("Namespace: %s\n", namespace)
|
|
||||||
|
|
||||||
tmpl, err := template.New("dnsCheckYAML").Parse(dnsCheckYAML)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("cannot parse template: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tempYaml, err := ioutil.TempFile("", "")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("cannot create file: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
api.LogDebugMessage("Temp Yaml file: %s\n", tempYaml.Name())
|
|
||||||
|
|
||||||
b := bytes.Buffer{}
|
|
||||||
err = tmpl.Execute(&b, namespace)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error: unable to create a kubernetes client: %v\n", err)
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tempYaml.WriteString(b.String())
|
// creating deployment
|
||||||
|
depName := "dep-dns-preflight-check"
|
||||||
// creating Kubectl resources
|
nginxImageName, err := qp.GetPreflightConfigObj().GetImageName(nginx, true)
|
||||||
appName := "qnginx001"
|
|
||||||
const PreflightChecksDirName = "preflight_checks"
|
|
||||||
|
|
||||||
fmt.Println("Creating resources to run preflight checks")
|
|
||||||
|
|
||||||
// kubectl create deployment
|
|
||||||
opr := fmt.Sprintf("create deployment %s --image=nginx", appName)
|
|
||||||
err = initiateK8sOps(opr, namespace)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dnsDeployment, err := createPreflightTestDeployment(clientset, namespace, depName, nginxImageName)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error: unable to create deployment: %v\n", err)
|
||||||
|
fmt.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer deleteDeployment(clientset, namespace, depName)
|
||||||
|
|
||||||
|
if err := waitForDeployment(clientset, namespace, dnsDeployment); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// creating service
|
||||||
|
serviceName := "svc-dns-pf-check"
|
||||||
|
dnsService, err := createPreflightTestService(clientset, namespace, serviceName)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error: unable to create service : %s\n", serviceName)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer deleteService(clientset, namespace, serviceName)
|
||||||
|
|
||||||
|
// create a pod
|
||||||
|
podName := "pf-pod-1"
|
||||||
|
commandToRun := []string{"sh", "-c", "sleep 10; nc -z -v -w 1 " + dnsService.Name + " 80"}
|
||||||
|
netcatImageName, err := qp.GetPreflightConfigObj().GetImageName(netcat, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dnsPod, err := createPreflightTestPod(clientset, namespace, podName, netcatImageName, commandToRun)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error: unable to create pod : %s\n", podName)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer deletePod(clientset, namespace, podName)
|
||||||
|
|
||||||
|
if err := waitForPod(clientset, namespace, dnsPod); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(dnsPod.Spec.Containers) == 0 {
|
||||||
|
err := fmt.Errorf("error: there are no containers in the pod")
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
waitForPodToDie(clientset, namespace, dnsPod)
|
||||||
// Deleting deployment..
|
|
||||||
opr = fmt.Sprintf("delete deployment %s", appName)
|
|
||||||
// we want to delete the k8s resource here, we dont care a lot about an error here
|
|
||||||
_ = initiateK8sOps(opr, namespace)
|
|
||||||
api.LogDebugMessage("delete deployment executed")
|
|
||||||
}()
|
|
||||||
|
|
||||||
// create service
|
logStr, err := getPodLogs(clientset, dnsPod)
|
||||||
opr = fmt.Sprintf("create service clusterip %s --tcp=80:80", appName)
|
|
||||||
err = initiateK8sOps(opr, namespace)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error: unable to execute dns check in the cluster: %v", err)
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
if strings.HasSuffix(strings.TrimSpace(logStr), "succeeded!") {
|
||||||
// delete service
|
fmt.Println("Preflight DNS check: PASSED")
|
||||||
opr = fmt.Sprintf("delete service %s", appName)
|
} else {
|
||||||
// we want to delete the k8s resource here, we dont care a lot about an error here
|
err = fmt.Errorf("Expected response not found\n")
|
||||||
_ = initiateK8sOps(opr, namespace)
|
|
||||||
api.LogDebugMessage("delete service executed")
|
|
||||||
}()
|
|
||||||
|
|
||||||
//kubectl -n $namespace wait --for=condition=ready pod -l app=$appName --timeout=120s
|
|
||||||
opr = fmt.Sprintf("wait --for=condition=ready pod -l app=%s --timeout=120s", appName)
|
|
||||||
err = initiateK8sOps(opr, namespace)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
api.LogDebugMessage("kubectl wait executed")
|
|
||||||
|
|
||||||
// call preflight
|
fmt.Println("Completed preflight DNS check")
|
||||||
preflightCommand := filepath.Join(qp.Q.QliksenseHome, PreflightChecksDirName, preflightFileName)
|
fmt.Println("Cleaning up resources...")
|
||||||
|
|
||||||
err = invokePreflight(preflightCommand, tempYaml)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Println("DNS check completed, cleaning up resources now")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
87
pkg/preflight/mongo_check.go
Normal file
87
pkg/preflight/mongo_check.go
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
package preflight
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
mongo = "mongo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (qp *QliksensePreflight) CheckMongo(kubeConfigContents []byte, namespace, mongodbUrl string) error {
|
||||||
|
fmt.Printf("Preflight mongodb check: \n")
|
||||||
|
|
||||||
|
if mongodbUrl == "" {
|
||||||
|
// infer mongoDbUrl from currentCR
|
||||||
|
fmt.Println("MongoDbUri is empty, infer from CR")
|
||||||
|
qConfig := qapi.NewQConfig(qp.Q.QliksenseHome)
|
||||||
|
var currentCR *qapi.QliksenseCR
|
||||||
|
|
||||||
|
var err error
|
||||||
|
qConfig.SetNamespace(namespace)
|
||||||
|
currentCR, err = qConfig.GetCurrentCR()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Unable to retrieve current CR: %v\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
decryptedCR, err := qConfig.GetDecryptedCr(currentCR)
|
||||||
|
mongodbUrl = decryptedCR.Spec.GetFromSecrets("qliksense", "mongoDbUri")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("mongodbUrl: %s\n", mongodbUrl)
|
||||||
|
if err := qp.mongoConnCheck(kubeConfigContents, namespace, mongodbUrl); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println("Completed preflight mongodb check")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qp *QliksensePreflight) mongoConnCheck(kubeConfigContents []byte, namespace, mongodbUrl string) error {
|
||||||
|
clientset, _, err := getK8SClientSet(kubeConfigContents, "")
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error: unable to create a kubernetes client: %v\n", err)
|
||||||
|
fmt.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// create a pod
|
||||||
|
podName := "pf-mongo-pod"
|
||||||
|
commandToRun := []string{"sh", "-c", "sleep 10;mongo " + mongodbUrl}
|
||||||
|
imageName, err := qp.GetPreflightConfigObj().GetImageName(mongo, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
mongoPod, err := createPreflightTestPod(clientset, namespace, podName, imageName, commandToRun)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error: unable to create pod : %v\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer deletePod(clientset, namespace, podName)
|
||||||
|
|
||||||
|
if err := waitForPod(clientset, namespace, mongoPod); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(mongoPod.Spec.Containers) == 0 {
|
||||||
|
err := fmt.Errorf("error: there are no containers in the pod- %v\n", err)
|
||||||
|
fmt.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
waitForPodToDie(clientset, namespace, mongoPod)
|
||||||
|
logStr, err := getPodLogs(clientset, mongoPod)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error: unable to execute mongo check in the cluster: %v\n", err)
|
||||||
|
fmt.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
stringToCheck := "Implicit session:"
|
||||||
|
if strings.Contains(logStr, stringToCheck) {
|
||||||
|
fmt.Println("Preflight mongo check: PASSED")
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("Expected response not found\n")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -3,139 +3,68 @@ package preflight
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
"github.com/mitchellh/go-homedir"
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/qlik-oss/sense-installer/pkg/api"
|
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
|
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||||
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
|
apiv1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/api/rbac/v1beta1"
|
||||||
|
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
"k8s.io/client-go/rest"
|
||||||
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
|
"k8s.io/client-go/util/retry"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var gracePeriod int64 = 0
|
||||||
|
|
||||||
type QliksensePreflight struct {
|
type QliksensePreflight struct {
|
||||||
Q *qliksense.Qliksense
|
Q *qliksense.Qliksense
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
func (qp *QliksensePreflight) GetPreflightConfigObj() *api.PreflightConfig {
|
||||||
// preflight releases have the same version
|
return api.NewPreflightConfig(qp.Q.QliksenseHome)
|
||||||
preflightRelease = "v0.9.26"
|
|
||||||
preflightLinuxFile = "preflight_linux_amd64.tar.gz"
|
|
||||||
preflightMacFile = "preflight_darwin_amd64.tar.gz"
|
|
||||||
preflightWindowsFile = "preflight_windows_amd64.zip"
|
|
||||||
PreflightChecksDirName = "preflight_checks"
|
|
||||||
preflightFileName = "preflight"
|
|
||||||
)
|
|
||||||
|
|
||||||
var preflightBaseURL = fmt.Sprintf("https://github.com/replicatedhq/troubleshoot/releases/download/%s/", preflightRelease)
|
|
||||||
|
|
||||||
func (qp *QliksensePreflight) DownloadPreflight() error {
|
|
||||||
const preflightExecutable = "preflight"
|
|
||||||
|
|
||||||
preflightInstallDir := filepath.Join(qp.Q.QliksenseHome, PreflightChecksDirName)
|
|
||||||
platform := runtime.GOOS
|
|
||||||
|
|
||||||
exists, err := checkInstalled(preflightInstallDir, preflightExecutable)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("There has been an error when trying to determine if preflight installer exists")
|
|
||||||
log.Println(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if exists {
|
|
||||||
// preflight exist, no need to download again.
|
|
||||||
api.LogDebugMessage("Preflight already exists, proceeding to perform checks")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the Preflight-check directory, download and install preflight
|
|
||||||
if !api.DirExists(preflightInstallDir) {
|
|
||||||
api.LogDebugMessage("%s does not exist, creating now\n", preflightInstallDir)
|
|
||||||
if err := os.Mkdir(preflightInstallDir, os.ModePerm); err != nil {
|
|
||||||
err = fmt.Errorf("Not able to create %s dir: %v", preflightInstallDir, err)
|
|
||||||
log.Println(err)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
api.LogDebugMessage("Preflight-checks install Dir: %s exists", preflightInstallDir)
|
|
||||||
|
|
||||||
preflightUrl, preflightFile, err := determinePlatformSpecificUrls(platform)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("There was an error when trying to determine platform specific paths")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Download Preflight
|
|
||||||
err = downloadAndExplode(preflightUrl, preflightInstallDir, preflightFile)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Println("Downloaded Preflight")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkInstalled(preflightInstallDir, preflightExecutable string) (bool, error) {
|
func InitPreflight() (string, []byte, error) {
|
||||||
installerExists := true
|
api.LogDebugMessage("Reading .kube/config file...")
|
||||||
preflightInstaller := filepath.Join(preflightInstallDir, preflightExecutable)
|
|
||||||
if api.DirExists(preflightInstallDir) {
|
|
||||||
if !api.FileExists(preflightInstaller) {
|
|
||||||
installerExists = false
|
|
||||||
api.LogDebugMessage("Preflight install directory exists, but preflight installer does not exist")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
installerExists = false
|
|
||||||
}
|
|
||||||
return installerExists, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func downloadAndExplode(url, installDir, file string) error {
|
homeDir, err := homedir.Dir()
|
||||||
err := api.DownloadFile(url, installDir, file)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
err = fmt.Errorf("Unable to deduce home dir\n")
|
||||||
|
return "", nil, err
|
||||||
}
|
}
|
||||||
api.LogDebugMessage("Downloaded File: %s", file)
|
api.LogDebugMessage("Kube config location: %s\n\n", filepath.Join(homeDir, ".kube", "config"))
|
||||||
|
|
||||||
fileToUntar := filepath.Join(installDir, file)
|
kubeConfig := filepath.Join(homeDir, ".kube", "config")
|
||||||
api.LogDebugMessage("File to explode: %s", file)
|
kubeConfigContents, err := ioutil.ReadFile(kubeConfig)
|
||||||
|
|
||||||
err = api.ExplodePackage(installDir, fileToUntar)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
err = fmt.Errorf("Unable to deduce home dir\n")
|
||||||
|
return "", nil, err
|
||||||
}
|
}
|
||||||
|
// retrieve namespace
|
||||||
return nil
|
namespace := api.GetKubectlNamespace()
|
||||||
}
|
// if namespace comes back empty, we will run checks in the default namespace
|
||||||
|
if namespace == "" {
|
||||||
func determinePlatformSpecificUrls(platform string) (string, string, error) {
|
namespace = "default"
|
||||||
|
|
||||||
var preflightUrl, preflightFile string
|
|
||||||
|
|
||||||
if runtime.GOARCH != `amd64` {
|
|
||||||
err := fmt.Errorf("%s architecture is not supported", runtime.GOARCH)
|
|
||||||
return "", "", err
|
|
||||||
}
|
}
|
||||||
|
api.LogDebugMessage("Namespace: %s\n", namespace)
|
||||||
switch platform {
|
return namespace, kubeConfigContents, nil
|
||||||
case "windows":
|
|
||||||
preflightFile = preflightWindowsFile
|
|
||||||
case "darwin":
|
|
||||||
preflightFile = preflightMacFile
|
|
||||||
case "linux":
|
|
||||||
preflightFile = preflightLinuxFile
|
|
||||||
default:
|
|
||||||
err := fmt.Errorf("Unable to download the preflight executable for the underlying platform\n")
|
|
||||||
return "", "", err
|
|
||||||
}
|
|
||||||
preflightUrl = fmt.Sprintf("%s%s", preflightBaseURL, preflightFile)
|
|
||||||
|
|
||||||
return preflightUrl, preflightFile, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initiateK8sOps(opr, namespace string) error {
|
func initiateK8sOps(opr, namespace string) error {
|
||||||
opr1 := strings.Fields(opr)
|
opr1 := strings.Fields(opr)
|
||||||
err := api.KubectlDirectOps(opr1, namespace)
|
_, err := api.KubectlDirectOps(opr1, namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return err
|
return err
|
||||||
@@ -143,45 +72,574 @@ func initiateK8sOps(opr, namespace string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func invokePreflight(preflightCommand string, yamlFile *os.File) error {
|
func int32Ptr(i int32) *int32 { return &i }
|
||||||
arguments := []string{}
|
|
||||||
arguments = append(arguments, yamlFile.Name(), "--interactive=false")
|
|
||||||
cmd := exec.Command(preflightCommand, arguments...)
|
|
||||||
|
|
||||||
sterrBuffer := &bytes.Buffer{}
|
func retryOnError(mf func() error) error {
|
||||||
cmd.Stdout = sterrBuffer
|
return retry.OnError(wait.Backoff{
|
||||||
cmd.Stderr = sterrBuffer
|
Duration: 1 * time.Second,
|
||||||
if err := cmd.Run(); err != nil {
|
Factor: 1,
|
||||||
return fmt.Errorf("Error when running preflight command: %v\n", err)
|
Jitter: 0.1,
|
||||||
|
Steps: 5,
|
||||||
|
}, func(err error) bool {
|
||||||
|
return k8serrors.IsConflict(err) || k8serrors.IsGone(err) || k8serrors.IsServerTimeout(err) ||
|
||||||
|
k8serrors.IsServiceUnavailable(err) || k8serrors.IsTimeout(err) || k8serrors.IsTooManyRequests(err)
|
||||||
|
}, mf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getK8SClientSet(kubeconfig []byte, contextName string) (*kubernetes.Clientset, *rest.Config, error) {
|
||||||
|
var clientConfig *rest.Config
|
||||||
|
var err error
|
||||||
|
if len(kubeconfig) == 0 {
|
||||||
|
clientConfig, err = rest.InClusterConfig()
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Wrap(err, "Unable to load in-cluster kubeconfig")
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
config, err := clientcmd.Load(kubeconfig)
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Wrap(err, "Unable to load kubeconfig")
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if contextName != "" {
|
||||||
|
config.CurrentContext = contextName
|
||||||
|
}
|
||||||
|
clientConfig, err = clientcmd.NewDefaultClientConfig(*config, &clientcmd.ConfigOverrides{}).ClientConfig()
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Wrap(err, "Unable to create client config from config")
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ind := strings.Index(sterrBuffer.String(), "---")
|
clientset, err := kubernetes.NewForConfig(clientConfig)
|
||||||
output := sterrBuffer.String()
|
if err != nil {
|
||||||
if ind > -1 {
|
err = errors.Wrap(err, "Unable to create clientset")
|
||||||
output = fmt.Sprintf("%s\n%s", output[:ind], output[ind:])
|
fmt.Println(err)
|
||||||
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
fmt.Printf("%v\n", output)
|
return clientset, clientConfig, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Maybe good to retain this part in case we need to process the output in future.
|
func createPreflightTestDeployment(clientset *kubernetes.Clientset, namespace string, depName string, imageName string) (*appsv1.Deployment, error) {
|
||||||
// We are going to look for the first occurance of PASS or FAIL from the end
|
deploymentsClient := clientset.AppsV1().Deployments(namespace)
|
||||||
// there are also some space-like deceiving characters which are being hard to get by
|
deployment := &appsv1.Deployment{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Name: depName,
|
||||||
|
},
|
||||||
|
Spec: appsv1.DeploymentSpec{
|
||||||
|
Replicas: int32Ptr(1),
|
||||||
|
Selector: &v1.LabelSelector{
|
||||||
|
MatchLabels: map[string]string{
|
||||||
|
"app": "preflight-check",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Template: apiv1.PodTemplateSpec{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Labels: map[string]string{
|
||||||
|
"app": "preflight-check",
|
||||||
|
"label": "preflight-check-label",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: apiv1.PodSpec{
|
||||||
|
Containers: []apiv1.Container{
|
||||||
|
{
|
||||||
|
Name: "dep",
|
||||||
|
Image: imageName,
|
||||||
|
Ports: []apiv1.ContainerPort{
|
||||||
|
{
|
||||||
|
Name: "http",
|
||||||
|
Protocol: apiv1.ProtocolTCP,
|
||||||
|
ContainerPort: 80,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
//outputArr := strings.Fields(strings.TrimSpace(output))
|
// Create Deployment
|
||||||
//trackSuccess := false
|
var result *appsv1.Deployment
|
||||||
//trackPrg := false
|
if err := retryOnError(func() (err error) {
|
||||||
|
result, err = deploymentsClient.Create(deployment)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
err = errors.Wrapf(err, "error: unable to create deployments in the %s namespace", namespace)
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fmt.Printf("Created deployment %q\n", result.GetObjectMeta().GetName())
|
||||||
|
|
||||||
//for i := len(outputArr) - 1; i >= 0; i-- {
|
return deployment, nil
|
||||||
// if strings.TrimSpace(outputArr[i]) != "" {
|
}
|
||||||
// if outputArr[i] == "PASS" {
|
|
||||||
// trackSuccess = true
|
|
||||||
// trackPrg = true
|
|
||||||
// } else if outputArr[i] == "FAIL" {
|
|
||||||
// trackPrg = true
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// if trackPrg {
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
func getDeployment(clientset *kubernetes.Clientset, namespace, depName string) (*appsv1.Deployment, error) {
|
||||||
|
deploymentsClient := clientset.AppsV1().Deployments(namespace)
|
||||||
|
var deployment *appsv1.Deployment
|
||||||
|
if err := retryOnError(func() (err error) {
|
||||||
|
deployment, err = deploymentsClient.Get(depName, v1.GetOptions{})
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
err = errors.Wrapf(err, "error: unable to get deployments in the %s namespace", namespace)
|
||||||
|
api.LogDebugMessage("%v\n", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return deployment, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteDeployment(clientset *kubernetes.Clientset, namespace, name string) error {
|
||||||
|
deploymentsClient := clientset.AppsV1().Deployments(namespace)
|
||||||
|
// Create Deployment
|
||||||
|
deletePolicy := v1.DeletePropagationForeground
|
||||||
|
deleteOptions := v1.DeleteOptions{
|
||||||
|
PropagationPolicy: &deletePolicy,
|
||||||
|
GracePeriodSeconds: &gracePeriod,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := retryOnError(func() (err error) {
|
||||||
|
return deploymentsClient.Delete(name, &deleteOptions)
|
||||||
|
}); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := waitForDeploymentToDelete(clientset, namespace, name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("Deleted deployment: %s\n", name)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createPreflightTestService(clientset *kubernetes.Clientset, namespace string, svcName string) (*apiv1.Service, error) {
|
||||||
|
iptr := int32Ptr(80)
|
||||||
|
servicesClient := clientset.CoreV1().Services(namespace)
|
||||||
|
service := &apiv1.Service{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Name: svcName,
|
||||||
|
Namespace: namespace,
|
||||||
|
Labels: map[string]string{
|
||||||
|
"app": "preflight-check",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: apiv1.ServiceSpec{
|
||||||
|
Ports: []apiv1.ServicePort{
|
||||||
|
{Name: "port1",
|
||||||
|
Port: *iptr,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Selector: map[string]string{
|
||||||
|
"app": "preflight-check",
|
||||||
|
},
|
||||||
|
ClusterIP: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var result *apiv1.Service
|
||||||
|
if err := retryOnError(func() (err error) {
|
||||||
|
result, err = servicesClient.Create(service)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fmt.Printf("Created service %q\n", result.GetObjectMeta().GetName())
|
||||||
|
|
||||||
|
return service, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getService(clientset *kubernetes.Clientset, namespace, svcName string) (*apiv1.Service, error) {
|
||||||
|
servicesClient := clientset.CoreV1().Services(namespace)
|
||||||
|
var svc *apiv1.Service
|
||||||
|
if err := retryOnError(func() (err error) {
|
||||||
|
svc, err = servicesClient.Get(svcName, v1.GetOptions{})
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
err = errors.Wrapf(err, "unable to get services in the %s namespace", namespace)
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return svc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteService(clientset *kubernetes.Clientset, namespace, name string) error {
|
||||||
|
servicesClient := clientset.CoreV1().Services(namespace)
|
||||||
|
// Create Deployment
|
||||||
|
deletePolicy := v1.DeletePropagationForeground
|
||||||
|
deleteOptions := v1.DeleteOptions{
|
||||||
|
PropagationPolicy: &deletePolicy,
|
||||||
|
}
|
||||||
|
if err := retryOnError(func() (err error) {
|
||||||
|
return servicesClient.Delete(name, &deleteOptions)
|
||||||
|
}); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("Deleted service: %s\n", name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func deletePod(clientset *kubernetes.Clientset, namespace, name string) error {
|
||||||
|
|
||||||
|
podsClient := clientset.CoreV1().Pods(namespace)
|
||||||
|
deletePolicy := v1.DeletePropagationForeground
|
||||||
|
deleteOptions := v1.DeleteOptions{
|
||||||
|
PropagationPolicy: &deletePolicy,
|
||||||
|
GracePeriodSeconds: &gracePeriod,
|
||||||
|
}
|
||||||
|
if err := retryOnError(func() (err error) {
|
||||||
|
return podsClient.Delete(name, &deleteOptions)
|
||||||
|
}); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := waitForPodToDelete(clientset, namespace, name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("Deleted pod: %s\n", name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createPreflightTestPod(clientset *kubernetes.Clientset, namespace string, podName string, imageName string, commandToRun []string) (*apiv1.Pod, error) {
|
||||||
|
// build the pod definition we want to deploy
|
||||||
|
pod := &apiv1.Pod{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Name: podName,
|
||||||
|
Namespace: namespace,
|
||||||
|
Labels: map[string]string{
|
||||||
|
"app": "preflight",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Spec: apiv1.PodSpec{
|
||||||
|
RestartPolicy: apiv1.RestartPolicyNever,
|
||||||
|
Containers: []apiv1.Container{
|
||||||
|
{
|
||||||
|
Name: "cnt",
|
||||||
|
Image: imageName,
|
||||||
|
ImagePullPolicy: apiv1.PullIfNotPresent,
|
||||||
|
Command: commandToRun,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// now create the pod in kubernetes cluster using the clientset
|
||||||
|
if err := retryOnError(func() (err error) {
|
||||||
|
pod, err = clientset.CoreV1().Pods(namespace).Create(pod)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fmt.Printf("Created pod: %s\n", pod.Name)
|
||||||
|
return pod, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPod(clientset *kubernetes.Clientset, namespace, podName string) (*apiv1.Pod, error) {
|
||||||
|
api.LogDebugMessage("Fetching pod: %s\n", podName)
|
||||||
|
var pod *apiv1.Pod
|
||||||
|
if err := retryOnError(func() (err error) {
|
||||||
|
pod, err = clientset.CoreV1().Pods(namespace).Get(podName, v1.GetOptions{})
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
api.LogDebugMessage("%v\n", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return pod, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPodLogs(clientset *kubernetes.Clientset, pod *apiv1.Pod) (string, error) {
|
||||||
|
podLogOpts := apiv1.PodLogOptions{}
|
||||||
|
|
||||||
|
api.LogDebugMessage("Retrieving logs for pod: %s namespace: %s\n", pod.GetName(), pod.Namespace)
|
||||||
|
req := clientset.CoreV1().Pods(pod.Namespace).GetLogs(pod.Name, &podLogOpts)
|
||||||
|
podLogs, err := req.Stream()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer podLogs.Close()
|
||||||
|
time.Sleep(15 * time.Second)
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
_, err = io.Copy(buf, podLogs)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
api.LogDebugMessage("Log from pod: %s\n", buf.String())
|
||||||
|
return buf.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForResource(checkFunc func() (interface{}, error), validateFunc func(interface{}) bool) error {
|
||||||
|
timeout := time.NewTicker(2 * time.Minute)
|
||||||
|
defer timeout.Stop()
|
||||||
|
OUT:
|
||||||
|
for {
|
||||||
|
r, err := checkFunc()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-timeout.C:
|
||||||
|
break OUT
|
||||||
|
default:
|
||||||
|
if validateFunc(r) {
|
||||||
|
break OUT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForDeployment(clientset *kubernetes.Clientset, namespace string, pfDeployment *appsv1.Deployment) error {
|
||||||
|
var err error
|
||||||
|
depName := pfDeployment.GetName()
|
||||||
|
checkFunc := func() (interface{}, error) {
|
||||||
|
pfDeployment, err = getDeployment(clientset, namespace, depName)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error: unable to retrieve deployment: %s\n", depName)
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return pfDeployment, nil
|
||||||
|
}
|
||||||
|
validateFunc := func(data interface{}) bool {
|
||||||
|
d := data.(*appsv1.Deployment)
|
||||||
|
return int(d.Status.ReadyReplicas) > 0
|
||||||
|
}
|
||||||
|
if err := waitForResource(checkFunc, validateFunc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if int(pfDeployment.Status.ReadyReplicas) == 0 {
|
||||||
|
err = fmt.Errorf("error: deployment took longer than expected to spin up pods")
|
||||||
|
fmt.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForPod(clientset *kubernetes.Clientset, namespace string, pod *apiv1.Pod) error {
|
||||||
|
var err error
|
||||||
|
if len(pod.Spec.Containers) == 0 {
|
||||||
|
err = fmt.Errorf("error: there are no containers in the pod")
|
||||||
|
fmt.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
podName := pod.Name
|
||||||
|
checkFunc := func() (interface{}, error) {
|
||||||
|
pod, err = getPod(clientset, namespace, podName)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error: unable to retrieve %s pod by name", podName)
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return pod, nil
|
||||||
|
}
|
||||||
|
validateFunc := func(data interface{}) bool {
|
||||||
|
po := data.(*apiv1.Pod)
|
||||||
|
return len(po.Status.ContainerStatuses) > 0 && po.Status.ContainerStatuses[0].Ready
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := waitForResource(checkFunc, validateFunc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(pod.Status.ContainerStatuses) == 0 || !pod.Status.ContainerStatuses[0].Ready {
|
||||||
|
err = fmt.Errorf("error: container is taking much longer than expected")
|
||||||
|
fmt.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForPodToDie(clientset *kubernetes.Clientset, namespace string, pod *apiv1.Pod) error {
|
||||||
|
podName := pod.Name
|
||||||
|
checkFunc := func() (interface{}, error) {
|
||||||
|
po, err := getPod(clientset, namespace, podName)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("error: unable to retrieve %s pod by name", podName)
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
api.LogDebugMessage("pod status: %v\n", po.Status.Phase)
|
||||||
|
return po, nil
|
||||||
|
}
|
||||||
|
validateFunc := func(r interface{}) bool {
|
||||||
|
po := r.(*apiv1.Pod)
|
||||||
|
return po.Status.Phase == apiv1.PodFailed || po.Status.Phase == apiv1.PodSucceeded
|
||||||
|
}
|
||||||
|
if err := waitForResource(checkFunc, validateFunc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForPodToDelete(clientset *kubernetes.Clientset, namespace, podName string) error {
|
||||||
|
checkFunc := func() (interface{}, error) {
|
||||||
|
po, err := getPod(clientset, namespace, podName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return po, nil
|
||||||
|
}
|
||||||
|
validateFunc := func(po interface{}) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err := waitForResource(checkFunc, validateFunc); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := fmt.Errorf("error: delete pod is taking unusually long")
|
||||||
|
fmt.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForDeploymentToDelete(clientset *kubernetes.Clientset, namespace, deploymentName string) error {
|
||||||
|
checkFunc := func() (interface{}, error) {
|
||||||
|
dep, err := getDeployment(clientset, namespace, deploymentName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dep, nil
|
||||||
|
}
|
||||||
|
validateFunc := func(po interface{}) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if err := waitForResource(checkFunc, validateFunc); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err := fmt.Errorf("error: delete deployment is taking unusually long")
|
||||||
|
fmt.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func createPfRole(clientset *kubernetes.Clientset, namespace, roleName string) (*v1beta1.Role, error) {
|
||||||
|
// build the role defination we want to create
|
||||||
|
var role *v1beta1.Role
|
||||||
|
roleSpec := &v1beta1.Role{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Name: roleName,
|
||||||
|
Namespace: namespace,
|
||||||
|
Labels: map[string]string{
|
||||||
|
"app": "preflight",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Rules: []v1beta1.PolicyRule{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// now create the role in kubernetes cluster using the clientset
|
||||||
|
if err := retryOnError(func() (err error) {
|
||||||
|
role, err = clientset.RbacV1beta1().Roles(namespace).Create(roleSpec)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Created role: %s\n", role.Name)
|
||||||
|
|
||||||
|
return role, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteRole(clientset *kubernetes.Clientset, namespace string, role *v1beta1.Role) {
|
||||||
|
rolesClient := clientset.RbacV1beta1().Roles(namespace)
|
||||||
|
|
||||||
|
deletePolicy := v1.DeletePropagationForeground
|
||||||
|
deleteOptions := v1.DeleteOptions{
|
||||||
|
PropagationPolicy: &deletePolicy,
|
||||||
|
}
|
||||||
|
err := rolesClient.Delete(role.GetName(), &deleteOptions)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Deleted role: %s\n\n", role.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createPfRoleBinding(clientset *kubernetes.Clientset, namespace, roleBindingName string) (*v1beta1.RoleBinding, error) {
|
||||||
|
var roleBinding *v1beta1.RoleBinding
|
||||||
|
// build the rolebinding defination we want to create
|
||||||
|
roleBindingSpec := &v1beta1.RoleBinding{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Name: roleBindingName,
|
||||||
|
Namespace: namespace,
|
||||||
|
Labels: map[string]string{
|
||||||
|
"app": "demo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Subjects: []v1beta1.Subject{
|
||||||
|
{
|
||||||
|
Kind: "ServiceAccount",
|
||||||
|
APIGroup: "",
|
||||||
|
Name: "preflight-check-subject",
|
||||||
|
Namespace: namespace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RoleRef: v1beta1.RoleRef{
|
||||||
|
APIGroup: "",
|
||||||
|
Kind: "Role",
|
||||||
|
Name: "preflight-check-roleref",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// now create the roleBinding in kubernetes cluster using the clientset
|
||||||
|
if err := retryOnError(func() (err error) {
|
||||||
|
roleBinding, err = clientset.RbacV1beta1().RoleBindings(namespace).Create(roleBindingSpec)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fmt.Printf("Created RoleBinding: %s\n", roleBindingSpec.Name)
|
||||||
|
return roleBinding, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteRoleBinding(clientset *kubernetes.Clientset, namespace string, roleBinding *v1beta1.RoleBinding) {
|
||||||
|
roleBindingClient := clientset.RbacV1beta1().RoleBindings(namespace)
|
||||||
|
|
||||||
|
deletePolicy := v1.DeletePropagationForeground
|
||||||
|
deleteOptions := v1.DeleteOptions{
|
||||||
|
PropagationPolicy: &deletePolicy,
|
||||||
|
}
|
||||||
|
err := roleBindingClient.Delete(roleBinding.GetName(), &deleteOptions)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Deleted RoleBinding: %s\n\n", roleBinding.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createPfServiceAccount(clientset *kubernetes.Clientset, namespace, serviceAccountName string) (*apiv1.ServiceAccount, error) {
|
||||||
|
var serviceAccount *apiv1.ServiceAccount
|
||||||
|
// build the serviceAccount defination we want to create
|
||||||
|
serviceAccountSpec := &apiv1.ServiceAccount{
|
||||||
|
ObjectMeta: v1.ObjectMeta{
|
||||||
|
Name: "preflight-check-test-serviceaccount",
|
||||||
|
Namespace: namespace,
|
||||||
|
Labels: map[string]string{
|
||||||
|
"app": "demo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// now create the serviceAccount in kubernetes cluster using the clientset
|
||||||
|
if err := retryOnError(func() (err error) {
|
||||||
|
serviceAccount, err = clientset.CoreV1().ServiceAccounts(namespace).Create(serviceAccountSpec)
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fmt.Printf("Created Service Account: %s\n", serviceAccountSpec.Name)
|
||||||
|
return serviceAccount, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteServiceAccount(clientset *kubernetes.Clientset, namespace string, serviceAccount *apiv1.ServiceAccount) {
|
||||||
|
serviceAccountClient := clientset.CoreV1().ServiceAccounts(namespace)
|
||||||
|
|
||||||
|
deletePolicy := v1.DeletePropagationForeground
|
||||||
|
deleteOptions := v1.DeleteOptions{
|
||||||
|
PropagationPolicy: &deletePolicy,
|
||||||
|
}
|
||||||
|
err := serviceAccountClient.Delete(serviceAccount.GetName(), &deleteOptions)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Deleted ServiceAccount: %s\n\n", serviceAccount.Name)
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ func Test_initiateK8sOps(t *testing.T) {
|
|||||||
name: "valid case",
|
name: "valid case",
|
||||||
args: args{
|
args: args{
|
||||||
opr: fmt.Sprintf("version"),
|
opr: fmt.Sprintf("version"),
|
||||||
namespace: "ash-ns",
|
namespace: "test-ns",
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
@@ -28,7 +28,7 @@ func Test_initiateK8sOps(t *testing.T) {
|
|||||||
name: "invalid case",
|
name: "invalid case",
|
||||||
args: args{
|
args: args{
|
||||||
opr: fmt.Sprintf("versions"),
|
opr: fmt.Sprintf("versions"),
|
||||||
namespace: "ash-ns",
|
namespace: "test-ns",
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
@@ -41,50 +41,3 @@ func Test_initiateK8sOps(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_determinePlatformSpecificUrls(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
platform string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
want string
|
|
||||||
want1 string
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "valid platform",
|
|
||||||
args: args{
|
|
||||||
platform: "windows",
|
|
||||||
},
|
|
||||||
want: fmt.Sprintf("%s%s", preflightBaseURL, preflightWindowsFile),
|
|
||||||
want1: preflightWindowsFile,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid platform",
|
|
||||||
args: args{
|
|
||||||
platform: "unix",
|
|
||||||
},
|
|
||||||
want: "",
|
|
||||||
want1: "",
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
got, got1, err := determinePlatformSpecificUrls(tt.args.platform)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("determinePlatformSpecificUrls() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if got != tt.want {
|
|
||||||
t.Errorf("determinePlatformSpecificUrls() got = %v, want %v", got, tt.want)
|
|
||||||
}
|
|
||||||
if got1 != tt.want1 {
|
|
||||||
t.Errorf("determinePlatformSpecificUrls() got1 = %v, want %v", got1, tt.want1)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
137
pkg/preflight/role_check.go
Normal file
137
pkg/preflight/role_check.go
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
package preflight
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
|
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
|
"github.com/qlik-oss/sense-installer/pkg/qliksense"
|
||||||
|
)
|
||||||
|
|
||||||
|
var resultYamlBytes = []byte("")
|
||||||
|
|
||||||
|
func (qp *QliksensePreflight) CheckCreateRole(namespace string) error {
|
||||||
|
// create a Role
|
||||||
|
fmt.Printf("Preflight role check: \n")
|
||||||
|
err := qp.checkCreateEntity(namespace, "Role")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println("Completed preflight role check")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qp *QliksensePreflight) CheckCreateRoleBinding(namespace string) error {
|
||||||
|
// create a RoleBinding
|
||||||
|
fmt.Printf("Preflight rolebinding check: \n")
|
||||||
|
err := qp.checkCreateEntity(namespace, "RoleBinding")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println("Completed preflight rolebinding check")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qp *QliksensePreflight) CheckCreateServiceAccount(namespace string) error {
|
||||||
|
// create a service account
|
||||||
|
fmt.Printf("Preflight serviceaccount check: \n")
|
||||||
|
err := qp.checkCreateEntity(namespace, "ServiceAccount")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println("Completed preflight serviceaccount check")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (qp *QliksensePreflight) checkCreateEntity(namespace, entityToTest string) error {
|
||||||
|
qConfig := qapi.NewQConfig(qp.Q.QliksenseHome)
|
||||||
|
var currentCR *qapi.QliksenseCR
|
||||||
|
mfroot := ""
|
||||||
|
kusDir := ""
|
||||||
|
var err error
|
||||||
|
currentCR, err = qConfig.GetCurrentCR()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Unable to retrieve current CR: %v\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if currentCR.IsRepoExist() {
|
||||||
|
mfroot = currentCR.Spec.GetManifestsRoot()
|
||||||
|
} else if tempDownloadedDir, err := qliksense.DownloadFromGitRepoToTmpDir(qliksense.QLIK_GIT_REPO, "master"); err != nil {
|
||||||
|
fmt.Printf("Unable to Download from git repo to tmp dir: %v\n", err)
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
mfroot = tempDownloadedDir
|
||||||
|
}
|
||||||
|
|
||||||
|
if currentCR.Spec.Profile == "" {
|
||||||
|
kusDir = filepath.Join(mfroot, "manifests", "docker-desktop")
|
||||||
|
} else {
|
||||||
|
kusDir = filepath.Join(mfroot, "manifests", currentCR.Spec.Profile)
|
||||||
|
}
|
||||||
|
if len(resultYamlBytes) == 0 {
|
||||||
|
resultYamlBytes, err = qliksense.ExecuteKustomizeBuild(kusDir)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Unable to retrieve manifests from executing kustomize: %v\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sa := qliksense.GetYamlsFromMultiDoc(string(resultYamlBytes), entityToTest)
|
||||||
|
if sa != "" {
|
||||||
|
sa = strings.Replace(sa, "name: qliksense", "name: preflight", -1)
|
||||||
|
} else {
|
||||||
|
err := fmt.Errorf("Unable to retrieve yamls to apply on cluster")
|
||||||
|
fmt.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
namespace = "" // namespace is handled when generating the manifests
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
fmt.Println("Cleaning up resources")
|
||||||
|
api.KubectlDelete(sa, namespace)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Preflight cleanup failed!")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = api.KubectlApply(sa, namespace)
|
||||||
|
if err != nil {
|
||||||
|
err := fmt.Errorf("Failed to create entity on the cluster: %v", err)
|
||||||
|
fmt.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Preflight %s check: PASSED\n", entityToTest)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (qp *QliksensePreflight) CheckCreateRB(namespace string, kubeConfigContents []byte) error {
|
||||||
|
|
||||||
|
// create a role
|
||||||
|
fmt.Printf("Preflight createRole check: \n")
|
||||||
|
err := qp.checkCreateEntity(namespace, "Role")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Preflight role check: FAILED")
|
||||||
|
}
|
||||||
|
fmt.Printf("Completed preflight role check\n\n")
|
||||||
|
|
||||||
|
// create a roleBinding
|
||||||
|
fmt.Printf("Preflight rolebinding check: \n")
|
||||||
|
err = qp.checkCreateEntity(namespace, "RoleBinding")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Preflight rolebinding check: FAILED")
|
||||||
|
}
|
||||||
|
fmt.Printf("Completed preflight rolebinding check\n\n")
|
||||||
|
|
||||||
|
// create a service account
|
||||||
|
fmt.Printf("Preflight serviceaccount check: \n")
|
||||||
|
err = qp.checkCreateEntity(namespace, "ServiceAccount")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Preflight serviceaccount check: FAILED")
|
||||||
|
}
|
||||||
|
fmt.Printf("Completed preflight serviceaccount check\n\n")
|
||||||
|
|
||||||
|
fmt.Println("Preflight RB check: PASSED")
|
||||||
|
fmt.Println("Completed preflight CreateRB check")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -1,84 +1,56 @@
|
|||||||
package preflight
|
package preflight
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"path/filepath"
|
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"github.com/qlik-oss/sense-installer/pkg/api"
|
"github.com/Masterminds/semver/v3"
|
||||||
|
"k8s.io/apimachinery/pkg/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
const minK8sVersion = "1.11.0"
|
func (qp *QliksensePreflight) CheckK8sVersion(namespace string, kubeConfigContents []byte) error {
|
||||||
const checkVersionYAML = `
|
|
||||||
apiVersion: troubleshoot.replicated.com/v1beta1
|
|
||||||
kind: Preflight
|
|
||||||
metadata:
|
|
||||||
name: cluster-preflight-checks
|
|
||||||
namespace: {{ .namespace }}
|
|
||||||
spec:
|
|
||||||
analyzers:
|
|
||||||
- clusterVersion:
|
|
||||||
outcomes:
|
|
||||||
- fail:
|
|
||||||
when: "< {{ .minK8sVersion }}"
|
|
||||||
message: The application requires at least Kubernetes {{ .minK8sVersion }} or later.
|
|
||||||
uri: https://www.kubernetes.io
|
|
||||||
- pass:
|
|
||||||
when: ">= {{ .minK8sVersion }}"
|
|
||||||
message: Good to go.
|
|
||||||
`
|
|
||||||
|
|
||||||
func (qp *QliksensePreflight) CheckK8sVersion() error {
|
var currentVersion *semver.Version
|
||||||
// retrieve namespace
|
|
||||||
namespace := api.GetKubectlNamespace()
|
|
||||||
|
|
||||||
api.LogDebugMessage("Namespace: %s\n", namespace)
|
clientset, _, err := getK8SClientSet(kubeConfigContents, "")
|
||||||
|
|
||||||
tmpl, err := template.New("checkVersionYAML").Parse(checkVersionYAML)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("cannot parse template: %v", err)
|
err = fmt.Errorf("Unable to create clientset: %v\n", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
tempYaml, err := ioutil.TempFile("", "")
|
var serverVersion *version.Info
|
||||||
if err != nil {
|
if err := retryOnError(func() (err error) {
|
||||||
fmt.Printf("cannot create file: %v", err)
|
serverVersion, err = clientset.ServerVersion()
|
||||||
|
return err
|
||||||
|
}); err != nil {
|
||||||
|
err = fmt.Errorf("Unable to get server version: %v\n", err)
|
||||||
|
//fmt.Println(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
api.LogDebugMessage("Temp Yaml file: %s\n", tempYaml.Name())
|
fmt.Printf("Kubernetes API Server version: %s\n", serverVersion.String())
|
||||||
|
|
||||||
b := bytes.Buffer{}
|
// Compare K8s version on the cluster with minimum supported k8s version
|
||||||
err = tmpl.Execute(&b, map[string]string{
|
currentVersion, err = semver.NewVersion(serverVersion.String())
|
||||||
"namespace": namespace,
|
|
||||||
"minK8sVersion": minK8sVersion,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Unable to convert server version into semver version: %v\n", err)
|
||||||
|
//fmt.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
//fmt.Printf("Current K8s Version: %v\n", currentVersion)
|
||||||
|
|
||||||
|
minK8sVersionSemver, err := semver.NewVersion(qp.GetPreflightConfigObj().GetMinK8sVersion())
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("Unable to convert minimum Kubernetes version into semver version:%v\n", err)
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
tempYaml.WriteString(b.String())
|
if currentVersion.GreaterThan(minK8sVersionSemver) {
|
||||||
//api.LogDebugMessage("Temp yaml contents: %s", b.String())
|
//fmt.Printf("\n\nCurrent %s Component version: %s is less than minimum required version:%s\n", component, currentComponentVersion, componentVersionFromDependenciesYaml)
|
||||||
fmt.Printf("Minimum Kubernetes version supported: %s\n", minK8sVersion)
|
fmt.Printf("Current %s is greater than minimum required version:%s\n", currentVersion, minK8sVersionSemver)
|
||||||
|
fmt.Println("Preflight minimum kubernetes version check: PASSED")
|
||||||
// current kubectl version
|
} else {
|
||||||
opr := fmt.Sprintf("version")
|
fmt.Printf("Current %s is less than minimum required version:%s\n", currentVersion, minK8sVersionSemver)
|
||||||
err = initiateK8sOps(opr, namespace)
|
fmt.Println("Preflight minimum kubernetes version check: FAILED")
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
fmt.Printf("Completed Preflight kubernetes minimum version check\n")
|
||||||
// call preflight
|
|
||||||
preflightCommand := filepath.Join(qp.Q.QliksenseHome, PreflightChecksDirName, preflightFileName)
|
|
||||||
|
|
||||||
err = invokePreflight(preflightCommand, tempYaml)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Minimum kubernetes version check completed")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ func (q *Qliksense) getConfigDirectory(gitUrl, gitRef, profileEntered string) (d
|
|||||||
}
|
}
|
||||||
|
|
||||||
if gitRef != "" {
|
if gitRef != "" {
|
||||||
if dir, err = downloadFromGitRepoToTmpDir(gitUrl, gitRef); err != nil {
|
if dir, err = DownloadFromGitRepoToTmpDir(gitUrl, gitRef); err != nil {
|
||||||
return "", false, "", err
|
return "", false, "", err
|
||||||
} else {
|
} else {
|
||||||
return dir, true, profile, nil
|
return dir, true, profile, nil
|
||||||
@@ -120,14 +120,15 @@ func (q *Qliksense) getConfigDirectory(gitUrl, gitRef, profileEntered string) (d
|
|||||||
return dir, false, profile, nil
|
return dir, false, profile, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if dir, err = downloadFromGitRepoToTmpDir(gitUrl, "master"); err != nil {
|
if dir, err = DownloadFromGitRepoToTmpDir(gitUrl, "master"); err != nil {
|
||||||
return "", false, "", err
|
return "", false, "", err
|
||||||
} else {
|
} else {
|
||||||
return dir, true, profile, nil
|
return dir, true, profile, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func downloadFromGitRepoToTmpDir(gitUrl, gitRef string) (string, error) {
|
//DownloadFromGitRepoToTmpDir download git repo to a temporary directory
|
||||||
|
func DownloadFromGitRepoToTmpDir(gitUrl, gitRef string) (string, error) {
|
||||||
if tmpDir, err := ioutil.TempDir("", ""); err != nil {
|
if tmpDir, err := ioutil.TempDir("", ""); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
} else {
|
} else {
|
||||||
@@ -193,8 +194,10 @@ func getImageList(yamlContent []byte) ([]string, error) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
var sortedImageList []string
|
var sortedImageList []string
|
||||||
for image, _ := range imageMap {
|
for image, v := range imageMap {
|
||||||
sortedImageList = append(sortedImageList, image)
|
sortedImageList = append(sortedImageList, image)
|
||||||
|
// a warning "simplify range expression" if written like this 'for image _ :=range imageMap'
|
||||||
|
_ = v
|
||||||
}
|
}
|
||||||
sort.Strings(sortedImageList)
|
sort.Strings(sortedImageList)
|
||||||
return sortedImageList, nil
|
return sortedImageList, nil
|
||||||
|
|||||||
@@ -525,12 +525,16 @@ func Test_About_getConfigDirectory(t *testing.T) {
|
|||||||
if err := q.SetUpQliksenseDefaultContext(); err != nil {
|
if err := q.SetUpQliksenseDefaultContext(); err != nil {
|
||||||
t.Fatalf("error setting up default context in the tmp dir: %v\n", err)
|
t.Fatalf("error setting up default context in the tmp dir: %v\n", err)
|
||||||
return nil, "", "", ""
|
return nil, "", "", ""
|
||||||
} else if err := q.FetchQK8s("master"); err != nil {
|
} else if qConfig, err := qapi.NewQConfigE(q.QliksenseHome); err != nil {
|
||||||
t.Fatalf("error fetching master config to the tmp dir: %v\n", err)
|
t.Fatalf("cannot initiallize qConfig: %v\n", err)
|
||||||
return nil, "", "", ""
|
return nil, "", "", ""
|
||||||
} else {
|
} else if !qConfig.IsRepoExistForCurrent("master") {
|
||||||
return q, "no-git-clone-for-you", "", ""
|
if err := q.FetchQK8s("master"); err != nil {
|
||||||
|
t.Fatalf("error fetching master config to the tmp dir: %v\n", err)
|
||||||
|
return nil, "", "", ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return q, "no-git-clone-for-you", "", ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
verify: func(q *Qliksense, configDir string, isTemporary bool, profile string) (ok bool, reason string, err error) {
|
verify: func(q *Qliksense, configDir string, isTemporary bool, profile string) (ok bool, reason string, err error) {
|
||||||
|
|||||||
@@ -15,8 +15,32 @@ func (q *Qliksense) ApplyCRFromReader(r io.Reader, opts *InstallCommandOptions,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := q.InstallQK8s(cr.GetLabelFromCr("version"), opts, keepPatchFiles); err != nil {
|
if IsQliksenseInstalled(cr.GetName()) {
|
||||||
return err
|
// it is needed in case want to upgrade from one version to another
|
||||||
|
if cr.Spec.ManifestsRoot == "" && cr.Spec.Git == nil {
|
||||||
|
v := cr.GetLabelFromCr("version")
|
||||||
|
if !qConfig.IsRepoExistForCurrent(v) {
|
||||||
|
if err := q.FetchQK8s(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return q.UpgradeQK8s(keepPatchFiles)
|
||||||
}
|
}
|
||||||
return nil
|
return q.InstallQK8s(cr.GetLabelFromCr("version"), opts, keepPatchFiles)
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsQliksenseInstalled(crName string) bool {
|
||||||
|
args := []string{
|
||||||
|
"get",
|
||||||
|
"qliksense",
|
||||||
|
crName,
|
||||||
|
"-ogo-template",
|
||||||
|
`--template='{{ .metadata.name}}'`,
|
||||||
|
}
|
||||||
|
_, err := qapi.KubectlDirectOps(args, "")
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -17,7 +18,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Q_INIT_CRD_PATH = "manifests/base/manifests/qliksense-init"
|
Q_INIT_CRD_PATH = "manifests/base/crds"
|
||||||
agreementTempalte = `
|
agreementTempalte = `
|
||||||
Please read the agreement at https://www.qlik.com/us/legal/license-terms
|
Please read the agreement at https://www.qlik.com/us/legal/license-terms
|
||||||
Accept the end user license agreement by providing acceptEULA=yes
|
Accept the end user license agreement by providing acceptEULA=yes
|
||||||
@@ -88,7 +89,7 @@ func (q *Qliksense) applyConfigToK8s(qcr *qapi.QliksenseCR) error {
|
|||||||
cr.GeneratePatches(&qcr.KApiCr, path.Join(userHomeDir, ".kube", "config"))
|
cr.GeneratePatches(&qcr.KApiCr, path.Join(userHomeDir, ".kube", "config"))
|
||||||
// apply generated manifests
|
// apply generated manifests
|
||||||
profilePath := filepath.Join(qcr.Spec.GetManifestsRoot(), qcr.Spec.GetProfileDir())
|
profilePath := filepath.Join(qcr.Spec.GetManifestsRoot(), qcr.Spec.GetProfileDir())
|
||||||
mByte, err := executeKustomizeBuild(profilePath)
|
mByte, err := ExecuteKustomizeBuild(profilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("cannot generate manifests for "+profilePath, err)
|
fmt.Println("cannot generate manifests for "+profilePath, err)
|
||||||
return err
|
return err
|
||||||
@@ -145,21 +146,76 @@ func (q *Qliksense) getCurrentCrDependentResourceAsString() (string, error) {
|
|||||||
var crString strings.Builder
|
var crString strings.Builder
|
||||||
|
|
||||||
for svcName, v := range qcr.Spec.Secrets {
|
for svcName, v := range qcr.Spec.Secrets {
|
||||||
|
hasFile := false
|
||||||
for _, item := range v {
|
for _, item := range v {
|
||||||
if item.ValueFrom != nil && item.ValueFrom.SecretKeyRef != nil {
|
if item.ValueFrom != nil && item.ValueFrom.SecretKeyRef != nil {
|
||||||
secretFilePath := filepath.Join(q.QliksenseHome, QliksenseContextsDir, qcr.GetName(), QliksenseSecretsDir, svcName+".yaml")
|
hasFile = true
|
||||||
|
break
|
||||||
if api.FileExists(secretFilePath) {
|
}
|
||||||
secretFile, err := ioutil.ReadFile(secretFilePath)
|
}
|
||||||
if err != nil {
|
if hasFile {
|
||||||
return "", err
|
secretFilePath := filepath.Join(q.QliksenseHome, QliksenseContextsDir, qcr.GetName(), QliksenseSecretsDir, svcName+".yaml")
|
||||||
}
|
if api.FileExists(secretFilePath) {
|
||||||
crString.WriteString("\n---\n")
|
secretFile, err := ioutil.ReadFile(secretFilePath)
|
||||||
crString.Write(secretFile)
|
if err != nil {
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
|
crString.WriteString("\n---\n")
|
||||||
|
crString.Write(secretFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
crString.WriteString("\n---\n")
|
crString.WriteString("\n---\n")
|
||||||
return crString.String(), nil
|
return crString.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *Qliksense) EditCR(contextName string) error {
|
||||||
|
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||||
|
if contextName == "" {
|
||||||
|
cr, err := qConfig.GetCurrentCR()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
contextName = cr.GetName()
|
||||||
|
}
|
||||||
|
crFilePath := qConfig.GetCRFilePath(contextName)
|
||||||
|
tempFile, err := ioutil.TempFile("", "*.yaml")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
crContent, err := ioutil.ReadFile(crFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ioutil.WriteFile(tempFile.Name(), crContent, os.ModePerm); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cmd := exec.Command(getKubeEditorTool(), tempFile.Name())
|
||||||
|
cmd.Stdin = os.Stdin
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
err = cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
newCr, err := qapi.GetCRObject(tempFile.Name())
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("cannot save the cr. Someting wrong in the file format. It is not saved\n" + err.Error())
|
||||||
|
}
|
||||||
|
oldCr, err := qapi.GetCRObject(crFilePath)
|
||||||
|
|
||||||
|
if oldCr.GetName() != newCr.GetName() {
|
||||||
|
return errors.New("cr name cannot be chagned")
|
||||||
|
}
|
||||||
|
if newCr.Validate() {
|
||||||
|
return qConfig.WriteCR(newCr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getKubeEditorTool() string {
|
||||||
|
editor := os.Getenv("KUBE_EDITOR")
|
||||||
|
if editor == "" {
|
||||||
|
editor = "vim"
|
||||||
|
}
|
||||||
|
return editor
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,19 +1,21 @@
|
|||||||
package qliksense
|
package qliksense
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rsa"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/qlik-oss/k-apis/pkg/config"
|
||||||
|
"github.com/robfig/cron/v3"
|
||||||
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
"github.com/robfig/cron/v3"
|
|
||||||
|
|
||||||
"github.com/qlik-oss/k-apis/pkg/config"
|
|
||||||
|
|
||||||
b64 "encoding/base64"
|
b64 "encoding/base64"
|
||||||
|
|
||||||
ansi "github.com/mattn/go-colorable"
|
ansi "github.com/mattn/go-colorable"
|
||||||
@@ -32,48 +34,58 @@ const (
|
|||||||
MaxContextNameLength = 17
|
MaxContextNameLength = 17
|
||||||
QliksenseSecretsDir = "secrets"
|
QliksenseSecretsDir = "secrets"
|
||||||
|
|
||||||
imageRegistryConfigKey = "imageRegistry"
|
imageRegistryConfigKey = "imageRegistry"
|
||||||
pullSecretName = "artifactory-docker-secret"
|
pullSecretName = "artifactory-docker-secret"
|
||||||
|
qliksenseOperatorImageRepo = "qlik-docker-oss.bintray.io"
|
||||||
|
qliksenseOperatorImageName = "qliksense-operator"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (q *Qliksense) SetSecretsFromReader(arg string, reader io.Reader, createSecret, base64Encoded bool) error {
|
||||||
|
//take only name from the arguments, value should be from reader
|
||||||
|
argName := strings.SplitN(arg, "=", 1)
|
||||||
|
if len(argName) != 1 {
|
||||||
|
return errors.New("can only have one argument from pipe")
|
||||||
|
}
|
||||||
|
valueBytes, err := ioutil.ReadAll(reader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return q.SetSecrets([]string{argName[0] + "=" + string(valueBytes)}, createSecret, base64Encoded)
|
||||||
|
}
|
||||||
|
|
||||||
// SetSecrets - set-secrets <key>=<value> commands
|
// SetSecrets - set-secrets <key>=<value> commands
|
||||||
func (q *Qliksense) SetSecrets(args []string, isSecretSet bool) error {
|
func (q *Qliksense) SetSecrets(args []string, isSecretSet bool, base64Encoded bool) error {
|
||||||
qConfig := api.NewQConfig(q.QliksenseHome)
|
qConfig := api.NewQConfig(q.QliksenseHome)
|
||||||
qliksenseCR, qliksenseContextsFile, err := retrieveCurrentContextInfo(q)
|
qliksenseCR, err := qConfig.GetCurrentCR()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metadata name in qliksense CR is the name of the current context
|
// Metadata name in qliksense CR is the name of the current context
|
||||||
api.LogDebugMessage("Current context: %s", qliksenseCR.GetName())
|
api.LogDebugMessage("Current context: %s", qliksenseCR.GetName())
|
||||||
rsaPublicKey, _, err := qConfig.GetCurrentContextEncryptionKeyPair()
|
encryptionKey, err := qConfig.GetEncryptionKeyForCurrent()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
resultArgs, err := api.ProcessConfigArgs(args)
|
resultArgs, err := api.ProcessConfigArgs(args, base64Encoded)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, ra := range resultArgs {
|
for _, ra := range resultArgs {
|
||||||
api.LogDebugMessage("value args to be encrypted: %s", ra.Value)
|
api.LogDebugMessage("value args to be encrypted: %s", ra.Value)
|
||||||
if err := q.processSecret(ra, rsaPublicKey, qliksenseCR, isSecretSet); err != nil {
|
if err := q.processSecret(ra, encryptionKey, qliksenseCR, isSecretSet); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// write modified content into context-yaml
|
// write modified content into context-yaml
|
||||||
api.WriteToFile(&qliksenseCR, qliksenseContextsFile)
|
return qConfig.WriteCR(qliksenseCR)
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Qliksense) processSecret(ra *api.ServiceKeyValue, rsaPublicKey *rsa.PublicKey, qliksenseCR *api.QliksenseCR, isSecretSet bool) error {
|
func (q *Qliksense) processSecret(ra *api.ServiceKeyValue, encryptionKey string, qliksenseCR *api.QliksenseCR, isSecretSet bool) error {
|
||||||
// encrypt value with RSA key pair
|
cipherText, e2 := api.EncryptData([]byte(ra.Value), encryptionKey)
|
||||||
valueBytes := []byte(ra.Value)
|
|
||||||
cipherText, e2 := api.Encrypt(valueBytes, rsaPublicKey)
|
|
||||||
if e2 != nil {
|
if e2 != nil {
|
||||||
return e2
|
return e2
|
||||||
}
|
}
|
||||||
base64EncodedSecret := b64.StdEncoding.EncodeToString(cipherText)
|
|
||||||
secretName := ""
|
secretName := ""
|
||||||
if isSecretSet {
|
if isSecretSet {
|
||||||
secretFolder := qliksenseCR.GetK8sSecretsFolder(q.QliksenseHome)
|
secretFolder := qliksenseCR.GetK8sSecretsFolder(q.QliksenseHome)
|
||||||
@@ -105,7 +117,8 @@ func (q *Qliksense) processSecret(ra *api.ServiceKeyValue, rsaPublicKey *rsa.Pub
|
|||||||
if k8sSecret.Data == nil {
|
if k8sSecret.Data == nil {
|
||||||
k8sSecret.Data = map[string][]byte{}
|
k8sSecret.Data = map[string][]byte{}
|
||||||
}
|
}
|
||||||
k8sSecret.Data[ra.Key] = []byte(base64EncodedSecret)
|
// v1.Secret does enconding, so no need to encode again
|
||||||
|
k8sSecret.Data[ra.Key] = []byte(cipherText)
|
||||||
|
|
||||||
// Write secret to file
|
// Write secret to file
|
||||||
k8sSecretBytes, err := api.K8sSecretToYaml(k8sSecret)
|
k8sSecretBytes, err := api.K8sSecretToYaml(k8sSecret)
|
||||||
@@ -118,25 +131,36 @@ func (q *Qliksense) processSecret(ra *api.ServiceKeyValue, rsaPublicKey *rsa.Pub
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
api.LogDebugMessage("Created a Kubernetes secret")
|
api.LogDebugMessage("Created a Kubernetes secret")
|
||||||
|
|
||||||
// Prepare args to update CR in the next step
|
|
||||||
base64EncodedSecret = ""
|
|
||||||
}
|
}
|
||||||
|
base64EncodedSecret := b64.StdEncoding.EncodeToString([]byte(cipherText))
|
||||||
// write into CR the keyref of the secret
|
// write into CR the keyref of the secret
|
||||||
qliksenseCR.Spec.AddToSecrets(ra.SvcName, ra.Key, base64EncodedSecret, secretName)
|
qliksenseCR.Spec.AddToSecrets(ra.SvcName, ra.Key, base64EncodedSecret, secretName)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *Qliksense) SetConfigFromReader(arg string, reader io.Reader, base64Encoded bool) error {
|
||||||
|
//take only name from the arguments, value should be from reader
|
||||||
|
argName := strings.SplitN(arg, "=", 1)
|
||||||
|
if len(argName) != 1 {
|
||||||
|
return errors.New("can only have one argument from pipe")
|
||||||
|
}
|
||||||
|
valueBytes, err := ioutil.ReadAll(reader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return q.SetConfigs([]string{argName[0] + "=" + string(valueBytes)}, base64Encoded)
|
||||||
|
}
|
||||||
|
|
||||||
// SetConfigs - set-configs <key>=<value> commands
|
// SetConfigs - set-configs <key>=<value> commands
|
||||||
func (q *Qliksense) SetConfigs(args []string) error {
|
func (q *Qliksense) SetConfigs(args []string, base64Encoded bool) error {
|
||||||
// retieve current context from config.yaml
|
// retieve current context from config.yaml
|
||||||
qliksenseCR, qliksenseContextsFile, err := retrieveCurrentContextInfo(q)
|
qConfig := api.NewQConfig(q.QliksenseHome)
|
||||||
|
qliksenseCR, err := qConfig.GetCurrentCR()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
resultArgs, err := api.ProcessConfigArgs(args)
|
resultArgs, err := api.ProcessConfigArgs(args, base64Encoded)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -144,54 +168,66 @@ func (q *Qliksense) SetConfigs(args []string) error {
|
|||||||
qliksenseCR.Spec.AddToConfigs(ra.SvcName, ra.Key, ra.Value)
|
qliksenseCR.Spec.AddToConfigs(ra.SvcName, ra.Key, ra.Value)
|
||||||
}
|
}
|
||||||
// write modified content into context.yaml
|
// write modified content into context.yaml
|
||||||
api.WriteToFile(&qliksenseCR, qliksenseContextsFile)
|
return qConfig.WriteCR(qliksenseCR)
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func retrieveCurrentContextInfo(q *Qliksense) (*api.QliksenseCR, string, error) {
|
func caseInsenstiveFieldByName(v reflect.Value, name string) reflect.Value {
|
||||||
var qliksenseConfig api.QliksenseConfig
|
name = strings.ToLower(name)
|
||||||
qliksenseConfigFile := filepath.Join(q.QliksenseHome, QliksenseConfigFile)
|
return v.FieldByNameFunc(func(n string) bool { return strings.ToLower(n) == name })
|
||||||
if err := api.ReadFromFile(&qliksenseConfig, qliksenseConfigFile); err != nil {
|
}
|
||||||
log.Println(err)
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
currentContext := qliksenseConfig.Spec.CurrentContext
|
|
||||||
api.LogDebugMessage("Current-context from config.yaml: %s", currentContext)
|
|
||||||
if currentContext == "" {
|
|
||||||
// current-context is empty
|
|
||||||
err := fmt.Errorf(`Please run the "qliksense config set-context <context-name>" first before viewing the current context info`)
|
|
||||||
log.Println(err)
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
// read the context.yaml file
|
|
||||||
qliksenseCR := &api.QliksenseCR{}
|
|
||||||
if currentContext == "" {
|
|
||||||
// current-context is empty
|
|
||||||
err := fmt.Errorf(`Please run the "qliksense config set-context <context-name>" first before viewing the current context info`)
|
|
||||||
log.Println(err)
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
qliksenseContextsFile := filepath.Join(q.QliksenseHome, QliksenseContextsDir, currentContext, currentContext+".yaml")
|
|
||||||
if !api.FileExists(qliksenseContextsFile) {
|
|
||||||
err := fmt.Errorf("Context file does not exist.\nPlease try re-running `qliksense config set-context <context-name>` and then `qliksense config view` again")
|
|
||||||
log.Println(err)
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
if err := api.ReadFromFile(qliksenseCR, qliksenseContextsFile); err != nil {
|
|
||||||
log.Println(err)
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
api.LogDebugMessage("Read context file: %s, Read QliksenseCR: %v", qliksenseContextsFile, qliksenseCR)
|
func validateCR(key string, keySub string, value string, crSpec *api.QliksenseCR) (bool, *api.QliksenseCR) {
|
||||||
|
cr := reflect.ValueOf(crSpec.Spec)
|
||||||
return qliksenseCR, qliksenseContextsFile, nil
|
keyValid := caseInsenstiveFieldByName(reflect.Indirect(cr), key)
|
||||||
|
if !keyValid.IsValid() {
|
||||||
|
//not in main spec
|
||||||
|
fmt.Println(key, "is an invalid key")
|
||||||
|
return false, crSpec
|
||||||
|
} else if keySub == "" {
|
||||||
|
if key == "rotatekeys" {
|
||||||
|
if _, err := validateInput(value); err != nil {
|
||||||
|
return false, crSpec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// checks if it is git or gitops
|
||||||
|
if keySub != "" {
|
||||||
|
if !keyValid.IsNil() {
|
||||||
|
if !caseInsenstiveFieldByName(reflect.Indirect(keyValid), keySub).IsValid() {
|
||||||
|
fmt.Println(keySub, "is an invalid key")
|
||||||
|
return false, crSpec
|
||||||
|
} else {
|
||||||
|
// verify gitops enabled and gitops schedule
|
||||||
|
switch keySub {
|
||||||
|
case "schedule":
|
||||||
|
if _, err := cron.ParseStandard(value); err != nil {
|
||||||
|
fmt.Println("Please enter string with standard cron scheduling syntax ")
|
||||||
|
return false, crSpec
|
||||||
|
}
|
||||||
|
case "enabled":
|
||||||
|
if !strings.EqualFold(value, "yes") && !strings.EqualFold(value, "no") {
|
||||||
|
fmt.Println("Please use yes or no for key enabled")
|
||||||
|
return false, crSpec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch key {
|
||||||
|
case "gitops":
|
||||||
|
crSpec.Spec.GitOps = &config.GitOps{}
|
||||||
|
case "git":
|
||||||
|
crSpec.Spec.Git = &config.Repo{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, crSpec
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetOtherConfigs - set profile/storageclassname/git.repository/manifestRoot commands
|
// SetOtherConfigs - set profile/storageclassname/git.repository/manifestRoot commands
|
||||||
func (q *Qliksense) SetOtherConfigs(args []string) error {
|
func (q *Qliksense) SetOtherConfigs(args []string) error {
|
||||||
// retieve current context from config.yaml
|
// retieve current context from config.yaml
|
||||||
qliksenseCR, qliksenseContextsFile, err := retrieveCurrentContextInfo(q)
|
qConfig := api.NewQConfig(q.QliksenseHome)
|
||||||
|
qliksenseCR, err := qConfig.GetCurrentCR()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -204,72 +240,127 @@ func (q *Qliksense) SetOtherConfigs(args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, arg := range args {
|
for _, arg := range args {
|
||||||
argsString := strings.Split(arg, "=")
|
if strings.HasPrefix(arg, "fetchSource.") {
|
||||||
switch argsString[0] {
|
if err := q.processSetFetchSource(arg, qliksenseCR); err != nil {
|
||||||
case "profile":
|
|
||||||
qliksenseCR.Spec.Profile = argsString[1]
|
|
||||||
api.LogDebugMessage("Current profile after modification: %s ", qliksenseCR.Spec.Profile)
|
|
||||||
case "git.repository":
|
|
||||||
if qliksenseCR.Spec.Git == nil {
|
|
||||||
qliksenseCR.Spec.Git = &config.Repo{}
|
|
||||||
}
|
|
||||||
qliksenseCR.Spec.Git.Repository = argsString[1]
|
|
||||||
api.LogDebugMessage("Current git repository after modification: %s ", qliksenseCR.Spec.Git.Repository)
|
|
||||||
case "storageClassName":
|
|
||||||
qliksenseCR.Spec.StorageClassName = argsString[1]
|
|
||||||
api.LogDebugMessage("Current StorageClassName after modification: %s ", qliksenseCR.Spec.StorageClassName)
|
|
||||||
case "manifestsRoot":
|
|
||||||
qliksenseCR.Spec.ManifestsRoot = argsString[1]
|
|
||||||
case "rotateKeys":
|
|
||||||
rotateKeys, err := validateInput(argsString[1])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
qliksenseCR.Spec.RotateKeys = rotateKeys
|
} else if strings.HasPrefix(arg, "git.") {
|
||||||
api.LogDebugMessage("Current rotateKeys after modification: %s ", qliksenseCR.Spec.RotateKeys)
|
if err := q.processSetGit(arg, qliksenseCR); err != nil {
|
||||||
case "gitops.enabled":
|
|
||||||
if qliksenseCR.Spec.GitOps == nil {
|
|
||||||
qliksenseCR.Spec.GitOps = &config.GitOps{}
|
|
||||||
}
|
|
||||||
if strings.EqualFold(argsString[1], "yes") || strings.EqualFold(argsString[1], "no") {
|
|
||||||
qliksenseCR.Spec.GitOps.Enabled = argsString[1]
|
|
||||||
api.LogDebugMessage("Current gitOps enabled status : %s ", qliksenseCR.Spec.GitOps.Enabled)
|
|
||||||
} else {
|
|
||||||
err := fmt.Errorf("Please use yes or no")
|
|
||||||
log.Println(err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case "gitops.schedule":
|
} else if strings.HasPrefix(arg, "gitOps.") {
|
||||||
if qliksenseCR.Spec.GitOps == nil {
|
if err := q.processSetGitOps(arg, qliksenseCR); err != nil {
|
||||||
qliksenseCR.Spec.GitOps = &config.GitOps{}
|
|
||||||
}
|
|
||||||
if _, err := cron.ParseStandard(argsString[1]); err != nil {
|
|
||||||
err := fmt.Errorf("Please enter string with standard cron scheduling syntax ")
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
qliksenseCR.Spec.GitOps.Schedule = argsString[1]
|
} else {
|
||||||
api.LogDebugMessage("Current gitOps schedule is : %s ", qliksenseCR.Spec.GitOps.Schedule)
|
if err := processSetSingleArg(arg, qliksenseCR); err != nil {
|
||||||
case "gitops.watchbranch":
|
return err
|
||||||
if qliksenseCR.Spec.GitOps == nil {
|
|
||||||
qliksenseCR.Spec.GitOps = &config.GitOps{}
|
|
||||||
}
|
}
|
||||||
qliksenseCR.Spec.GitOps.WatchBranch = argsString[1]
|
}
|
||||||
api.LogDebugMessage("Current gitOps watchbranch is : %s ", qliksenseCR.Spec.GitOps.WatchBranch)
|
fmt.Println(chalk.Green.Color("Successfully added to Custom Resource Spec"))
|
||||||
case "gitops.image":
|
}
|
||||||
if qliksenseCR.Spec.GitOps == nil {
|
|
||||||
qliksenseCR.Spec.GitOps = &config.GitOps{}
|
// write modified content into context.yaml
|
||||||
|
return qConfig.WriteCR(qliksenseCR)
|
||||||
|
}
|
||||||
|
|
||||||
|
func processSetSingleArg(arg string, cr *api.QliksenseCR) error {
|
||||||
|
nv := strings.Split(arg, "=")
|
||||||
|
switch nv[0] {
|
||||||
|
case "manifestsRoot":
|
||||||
|
cr.Spec.ManifestsRoot = nv[1]
|
||||||
|
case "profile":
|
||||||
|
cr.Spec.Profile = nv[1]
|
||||||
|
case "storageClassName":
|
||||||
|
cr.Spec.StorageClassName = nv[1]
|
||||||
|
case "rotateKeys":
|
||||||
|
valid := false
|
||||||
|
for _, v := range []string{"yes", "no", "None"} {
|
||||||
|
if nv[1] == v {
|
||||||
|
valid = true
|
||||||
}
|
}
|
||||||
qliksenseCR.Spec.GitOps.Image = argsString[1]
|
}
|
||||||
api.LogDebugMessage("Current gitOps image is : %s ", qliksenseCR.Spec.GitOps.Image)
|
if !valid {
|
||||||
default:
|
return errors.New("please povide rotateKeys=yes|no|None")
|
||||||
err := fmt.Errorf("Please enter one of: profile, storageClassName,rotateKeys, manifestRoot, git.repository or gitops arguments to configure the current context")
|
}
|
||||||
log.Println(err)
|
cr.Spec.RotateKeys = nv[1]
|
||||||
|
default:
|
||||||
|
return errors.New("Please enter one of: profile, storageClassName,rotateKeys, manifestRoot to configure the current context")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Qliksense) processSetFetchSource(arg string, cr *api.QliksenseCR) error {
|
||||||
|
args := strings.Split(arg, "=")
|
||||||
|
subs := strings.Split(args[0], ".")
|
||||||
|
if cr.Spec.FetchSource == nil {
|
||||||
|
cr.Spec.FetchSource = &config.Repo{}
|
||||||
|
}
|
||||||
|
switch subs[1] {
|
||||||
|
case "repository":
|
||||||
|
cr.Spec.FetchSource.Repository = args[1]
|
||||||
|
case "accessToken":
|
||||||
|
qConfig := api.NewQConfig(q.QliksenseHome)
|
||||||
|
key, err := qConfig.GetEncryptionKeyFor(cr.GetName())
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
return cr.SetFetchAccessToken(args[1], key)
|
||||||
|
case "secretName":
|
||||||
|
cr.Spec.FetchSource.SecretName = args[1]
|
||||||
|
case "userName":
|
||||||
|
cr.Spec.FetchSource.UserName = args[1]
|
||||||
|
default:
|
||||||
|
return errors.New(arg + " does not match any cr spec")
|
||||||
}
|
}
|
||||||
// write modified content into context.yaml
|
return nil
|
||||||
api.WriteToFile(&qliksenseCR, qliksenseContextsFile)
|
}
|
||||||
|
|
||||||
|
func (q *Qliksense) processSetGit(arg string, cr *api.QliksenseCR) error {
|
||||||
|
args := strings.Split(arg, "=")
|
||||||
|
subs := strings.Split(args[0], ".")
|
||||||
|
if cr.Spec.Git == nil {
|
||||||
|
cr.Spec.Git = &config.Repo{}
|
||||||
|
}
|
||||||
|
switch subs[1] {
|
||||||
|
case "repository":
|
||||||
|
cr.Spec.Git.Repository = args[1]
|
||||||
|
case "accessToken":
|
||||||
|
cr.Spec.Git.AccessToken = args[1]
|
||||||
|
case "secretName":
|
||||||
|
cr.Spec.Git.SecretName = args[1]
|
||||||
|
case "userName":
|
||||||
|
cr.Spec.Git.UserName = args[1]
|
||||||
|
default:
|
||||||
|
return errors.New(arg + " does not match any cr spec")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Qliksense) processSetGitOps(arg string, cr *api.QliksenseCR) error {
|
||||||
|
args := strings.Split(arg, "=")
|
||||||
|
subs := strings.Split(args[0], ".")
|
||||||
|
if cr.Spec.Git == nil {
|
||||||
|
cr.Spec.GitOps = &config.GitOps{}
|
||||||
|
}
|
||||||
|
switch subs[1] {
|
||||||
|
case "enabled":
|
||||||
|
if args[1] != "yes" && args[1] != "no" {
|
||||||
|
return errors.New("Please use yes or no for key enabled")
|
||||||
|
}
|
||||||
|
cr.Spec.GitOps.Enabled = args[1]
|
||||||
|
case "schedule":
|
||||||
|
if _, err := cron.ParseStandard(args[1]); err != nil {
|
||||||
|
return errors.New("Please enter string with standard cron scheduling syntax ")
|
||||||
|
}
|
||||||
|
cr.Spec.GitOps.Schedule = args[1]
|
||||||
|
case "watchBranch":
|
||||||
|
cr.Spec.GitOps.WatchBranch = args[1]
|
||||||
|
case "image":
|
||||||
|
cr.Spec.GitOps.Image = args[1]
|
||||||
|
default:
|
||||||
|
return errors.New(arg + " does not match any cr spec")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,6 +465,12 @@ func (q *Qliksense) DeleteContextConfig(args []string) error {
|
|||||||
|
|
||||||
// SetUpQliksenseDefaultContext - to setup dir structure for default qliksense context
|
// SetUpQliksenseDefaultContext - to setup dir structure for default qliksense context
|
||||||
func (q *Qliksense) SetUpQliksenseDefaultContext() error {
|
func (q *Qliksense) SetUpQliksenseDefaultContext() error {
|
||||||
|
if api.FileExists(filepath.Join(q.QliksenseHome, "config.yaml")) {
|
||||||
|
qliksenseConfig := api.NewQConfig(q.QliksenseHome)
|
||||||
|
if qliksenseConfig.IsContextExist(DefaultQliksenseContext) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
return q.SetUpQliksenseContext(DefaultQliksenseContext)
|
return q.SetUpQliksenseContext(DefaultQliksenseContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,7 +501,8 @@ func (q *Qliksense) SetUpQliksenseContext(contextName string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if qliksenseConfig.IsContextExist(contextName) {
|
if qliksenseConfig.IsContextExist(contextName) {
|
||||||
return nil
|
qliksenseConfig.Spec.CurrentContext = contextName
|
||||||
|
return qliksenseConfig.Write()
|
||||||
}
|
}
|
||||||
qliksenseCR := &api.QliksenseCR{}
|
qliksenseCR := &api.QliksenseCR{}
|
||||||
qliksenseCR.AddCommonConfig(contextName)
|
qliksenseCR.AddCommonConfig(contextName)
|
||||||
@@ -414,7 +512,7 @@ func (q *Qliksense) SetUpQliksenseContext(contextName string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// set the encrypted default mongo
|
// set the encrypted default mongo
|
||||||
return q.SetSecrets([]string{`qliksense.mongoDbUri="mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"`}, false)
|
return q.SetSecrets([]string{`qliksense.mongoDbUri="mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"`}, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateInput(input string) (string, error) {
|
func validateInput(input string) (string, error) {
|
||||||
@@ -435,7 +533,8 @@ func validateInput(input string) (string, error) {
|
|||||||
return input, err
|
return input, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrepareK8sSecret decodes and decrypts the secret value in the secret.yaml file and returns a B64encoded string
|
// PrepareK8sSecret targetFile contains base64 encoded value of encrypted value.
|
||||||
|
// this method decodes and decrypts the secret value in the secret.yaml file and returns a B64encoded string
|
||||||
func (q *Qliksense) PrepareK8sSecret(targetFile string) (string, error) {
|
func (q *Qliksense) PrepareK8sSecret(targetFile string) (string, error) {
|
||||||
// check if targetFile exists
|
// check if targetFile exists
|
||||||
if !api.FileExists(targetFile) {
|
if !api.FileExists(targetFile) {
|
||||||
@@ -444,7 +543,7 @@ func (q *Qliksense) PrepareK8sSecret(targetFile string) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
qConfig := api.NewQConfig(q.QliksenseHome)
|
qConfig := api.NewQConfig(q.QliksenseHome)
|
||||||
_, rsaPrivateKey, err := qConfig.GetCurrentContextEncryptionKeyPair()
|
encryptionKey, err := qConfig.GetEncryptionKeyForCurrent()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -462,17 +561,13 @@ func (q *Qliksense) PrepareK8sSecret(targetFile string) (string, error) {
|
|||||||
dataMap := k8sSecret1.Data
|
dataMap := k8sSecret1.Data
|
||||||
var resultMap = make(map[string][]byte)
|
var resultMap = make(map[string][]byte)
|
||||||
for k, v := range dataMap {
|
for k, v := range dataMap {
|
||||||
ba, err := b64.StdEncoding.DecodeString(string(v))
|
//k8s secrets has already base64 decoed value
|
||||||
if err != nil {
|
decryptedString, err := api.DecryptData(v, encryptionKey)
|
||||||
err := fmt.Errorf("Not able to decode message: %v", err)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
decryptedString, err := api.Decrypt(ba, rsaPrivateKey)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("Not able to decrypt message: %v", err)
|
err := fmt.Errorf("Not able to decrypt message: %v", err)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
resultMap[k] = decryptedString
|
resultMap[k] = []byte(decryptedString)
|
||||||
}
|
}
|
||||||
|
|
||||||
// putting the above map back into the k8sSecret struct
|
// putting the above map back into the k8sSecret struct
|
||||||
@@ -496,7 +591,7 @@ func readTargetfile(targetFile string) ([]byte, error) {
|
|||||||
|
|
||||||
func (q *Qliksense) SetImageRegistry(registry, pushUsername, pushPassword, pullUsername, pullPassword string) error {
|
func (q *Qliksense) SetImageRegistry(registry, pushUsername, pushPassword, pullUsername, pullPassword string) error {
|
||||||
qConfig := api.NewQConfig(q.QliksenseHome)
|
qConfig := api.NewQConfig(q.QliksenseHome)
|
||||||
qliksenseCR, qliksenseContextsFile, err := retrieveCurrentContextInfo(q)
|
qliksenseCR, err := qConfig.GetCurrentCR()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -523,7 +618,7 @@ func (q *Qliksense) SetImageRegistry(registry, pushUsername, pushPassword, pullU
|
|||||||
}
|
}
|
||||||
|
|
||||||
qliksenseCR.Spec.AddToConfigs("qliksense", imageRegistryConfigKey, registry)
|
qliksenseCR.Spec.AddToConfigs("qliksense", imageRegistryConfigKey, registry)
|
||||||
return api.WriteToFile(&qliksenseCR, qliksenseContextsFile)
|
return qConfig.WriteCR(qliksenseCR)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Qliksense) SetEulaAccepted() error {
|
func (q *Qliksense) SetEulaAccepted() error {
|
||||||
|
|||||||
@@ -35,119 +35,35 @@ metadata:
|
|||||||
name: testctx-qliksense-senseinstaller
|
name: testctx-qliksense-senseinstaller
|
||||||
type: Opaque
|
type: Opaque
|
||||||
`
|
`
|
||||||
var encText = "SFpVZ2t5SGsrN2lLQjlTYm9rbFUxSDFRcmVYdUxhTW9MUHlQOGtGditxMEcwZTlIZDl1dVRrV0tEYm5qUURSWFp3dStuNklueGk3anI2c1djSVdsbWlKTHdWQUJwdUg0a1NXd3llMUlMa2oxK3FRSFlMM2dQUExvN1pBYkVDeDROMUVvam12M0t0NmQwbkdhSXlWWEpmWWJUVVFDM1Y4L0ZTVXBVN0JUb0l4OVZWdmlPam5HTHk4RlF2a3RUaHJxWTUvZEh2N3pVUmhiOTc2Q2YwbEovZ3I2L2NwRk9RMUFXVXdodVhrTG9lYjVzNFdtTEZzNldqT3k0bWlKM1J6VllLaWVUSFJ2SE85eDB6dUthanRwSGEzWEZkaE5QNnpySVJJNTRFalUyblVYYUNlYXVnWnZEOUxjdWluOFhFcjExbkFINURCUDAycXhoZk5BejVoMlV2eFNWVmR0aW1QTDBhMVBJTUxGQTgyWUkrQkFOQkhkSUNnZGU5SkxIRFBoTzR6c0llaE1LRmhVQkNoOUhQa3kyRnhTeDJ3YWp3M1UycEsvcFJVZUxDazRUbkhmL25LN3h5ekdpV3dSUFFFZHdsWE5JbUhjVlVPV3gvNWh4WlJCUTZtb3pGYk1HbXR1Mkh5Z3RVV2gzNFYzd1BhS01TNFRsa0hyODFjRjVCWVpxenBFK1pKWnVyLy8zbzJsU0tFMjMxTG1pcGk1K0FqbXZvUVcyWHBocjFNVWJQY1pXUkJFRkkyQXBCM0FhQXFPa0k1MkRqNG43Mko5bCtaMzdydTk1aHk5K1lzY0FxMjZVbExYRlc0S3RUUkRLSjlMNnVmdlIrUUNudER3em5UTFRHUnEwZU5COWt6S0Q4MFlUdXozeHNXK3cxdjlHbDJaMnBZMTZWTCtEV1k9"
|
|
||||||
var decText = "mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"
|
var decText = "mongodb://qlik-default-mongodb:27017/qliksense?ssl=false"
|
||||||
|
|
||||||
func setupTargetFileAndPrivateKey() ([]byte, []byte, string, error) {
|
func setupTargetFileAndPrivateKey() (string, string, error) {
|
||||||
targetFileString := fmt.Sprintf(targetFileStringTemplate, encText)
|
|
||||||
privKeyBytes := []byte(`-----BEGIN RSA PRIVATE KEY-----
|
|
||||||
MIIJJwIBAAKCAgEApFf3qCQhAr2QLRRZdhLyB8exLjrQiXLr8hwDe0xHSLJX3w7v
|
|
||||||
5z4ujJWrHUulQ2/hvS8uffxMVrp2YqeA4sjy/ku1KqZVQv/WNTdL2v9Z1ewbRnBj
|
|
||||||
DQvmkWDKZ+cP8VPdHGzQ4iM2z6BZ4RQTkdQMKqsVwUsLO9amI2TOny/M696eFRW0
|
|
||||||
pk4+W3QZZRawT0HqJPvKKXKqoO2+62W54rOV8glJi29Do06e4S6CZUl1hBUy0VlL
|
|
||||||
trLlRSOHTois0dF2a9f7+GGgU11MHO6w1k1NesSlfZ0vnrrkW8WFLqewk+Jj+w1x
|
|
||||||
eQnYHOeHjx6zi9f9DC96eSylSB3iJ71NmXcMc0IEZ2LiQIqTL83BLOgMBCsK3FSl
|
|
||||||
GMakQUR2GJ+M0I/selYkRMhid6eOmhlsTNMPbpcTHxZ+ReIzS+5B1X0FZ7RIL+jS
|
|
||||||
L9mFcxmD3dxurrrt/DkLpXcuWdi1s1bpVn3jIQhU0+bgA6hT0k8Kj2f3Q9QnvkHS
|
|
||||||
Eff+XyLvLwQeSsSAcnM+1I7fNSPEo2cq0au6ZtjHcirXmMminAQ6cKW1XrEvJBef
|
|
||||||
HHibtjJjIM4bHH7MKRA5H3km/J4CCwI1VogSTcE05Z6ypAFU2TCrnec9c9VXkRDP
|
|
||||||
94h0GuoG8sdhmQudqvghr/8T7CV+sGRQbdeqrXwanfnGPrjcMVIO/dSOxOUCAwEA
|
|
||||||
AQKCAgA5b2TmJnpC8u0IVCxPz582iNurRHLNFpTPMGsnFCl1hp6fHiFJt7mc+FGt
|
|
||||||
E1rWjqtd6rdc4Gfth40IPXIV0BTcOqk+FpOFrtO2FXU1PDixQqrlmzGCxb324NTc
|
|
||||||
KyyvMpf77yuxXI0zUt8WgmW0eV8nKlOYEhoC96lohTqQ96uuY0bsJ4HS/VVdsN2P
|
|
||||||
Lra/fFHQSw8EHUb0pyIqMoscZ5bn18cUK/Z/hGKSYCbCL0Iavy3bbFHBsBPgbeJD
|
|
||||||
2BBN4953Iiy1Sak2eUy4b9LtkmaZmVAc7mpOFxLn38gD3icgB+bZPoGBw6b7sw71
|
|
||||||
Pc2R+hI9x/oNj0TUR11adhZApBJ9RhBbnSCUt8OUt9U5prNj+9qs8cHJGywtz1da
|
|
||||||
ZT1M6mn2MFSsaOyOlJPzGUzSf4AhI7HiouDpLHtHDqLmc8Vv4rZUqrcFw6kZTCY5
|
|
||||||
564yE8hh/UimOgQr7467/hADHZ0kBsupFEDWRqQ3qTIikHmGhTYZehDrSGL/3BMG
|
|
||||||
rvsFdv0krUHyW4FfHqPN09jfP3LTqd5vVbzRhxcGsoGmP/1kXIDtO8Cp1s/K6Mse
|
|
||||||
tInRCRla8ttZ3CZZ+Vf8HLi/n8XSRfbnMGYi7lVVxnp6kNsTEBgosbdU/r1zbMRJ
|
|
||||||
8mMMHygyugaRLHmOD/8fkWLfyR88cxPH8u9NufTfvJgiFpZboQKCAQEA0uSU6IGZ
|
|
||||||
pXIVZdmDWt4mxpS5T2UYarw3V9z/Isd3kkUU5YrC2XrvPRwmx5Jh9GXl9WENYJAR
|
|
||||||
wH7PaJT0HpBwpxJa1RqHHDSka7DnDcy44oRXyM7e2AmcW8QvcDty/0HPo4oZq4IT
|
|
||||||
m/+ot1R+bIpmJOweGRhVauzxJEUlQyt+kiH/ad8GiOS6LwqFPq42alnUxPQ106wF
|
|
||||||
EZZ2WQdzkyV6tF9aMG18AT1fJsGwNjCLRxJ52t/aEUP5mYwlL2UTT5Acn8KbtrTO
|
|
||||||
fFLAxGuB9LDdT1tGgIpzsXmxAaaeuPvSK4TDFdQyLAUdQJdz0GD9j9ciMPQH3UPe
|
|
||||||
Vjt6qtpfY6QK2wKCAQEAx36Vys5BlQI0TG6qORI0fiOYpLG1GqmdbCNRgBUsMj5T
|
|
||||||
LFe7uSd4qnDvGmns4MdkSSOlpF17bQiWhWKbjKRQpT0U/46zcIT4pWyajXJe+i9H
|
|
||||||
M/DpSRkMq2kGkx6KX2u9L66QBzcxJjtS17amdSpDAfsrvJgOWkxxInzw9n1u6aTe
|
|
||||||
ZjRDXdVX0KjPebEPOaoJToxne21Od3t+47TnDsQPsO1dvvrXX76IfH8cAlD5+0C/
|
|
||||||
b2YvDqWDmh9ICjKShwuDWgi4KjCV5PMHCIxH0FQ2L6mSbwIb9YgGin3wjN3KbWqz
|
|
||||||
dgEu7MeDxEwxZSSg4OstYVLQVgM39G/2ZA4YVJEbPwKCAQAo9FjymhBzb6c2Izp+
|
|
||||||
D/wpvkIKaBCI0cpRlso5P9E5p466UOsr/tKs5GWnhgbdxlgVAebuJKw93KJ8pciO
|
|
||||||
kvA9kbPwBHnOgW6Ytz73kBUrcBX4GixueddSftPTkMfxSB+Bm9UGWHlkZw6lo5P1
|
|
||||||
kh7p9qyVpQMZg7AEoiTtWWn4CQAn2DbVqM17Syi7Fmvc1VsbcG1vkM1fMAAFpAvO
|
|
||||||
vI2Kr6W9F9XoC7oJtb15mI3DnJPrbGNVzQSQzAWAoblRTyQv5kQFBDHBNPTYcCRJ
|
|
||||||
l3sy6P/VAI4dHgvAzVGvjL+w0dRszct8fvXCUGceRWeYYmfyZ8GLN53a0ywsN8Ik
|
|
||||||
gHvXAoIBACee5HEa9bt6bJihgf1DuFk1CKPtB2L8PN+1RAKEMfrolexAoG/tfvGa
|
|
||||||
7GH6l6ks8KX2BnfWeST2h66GHw6Xs8ydjQYUeV7nidqQ70EYbfSSXznZpvt1liaU
|
|
||||||
/VFKx4CcDT7jFIfaVlCZh6KADB9I/XXvRIh4SqF0fSO0XMcXsmeE7watapPAQ2iV
|
|
||||||
nl804yk4tBB9oi/JTcQ9Kr5et2UfW15wRiYf+5ZwaPsQ46cyHfPgsCSXztDB3plF
|
|
||||||
jTE5ShC4IKZJBQqcC6kk+0ifU8P0da6RpxuU96iUE3h9+sB/bCy+/FV7dq5gEbNy
|
|
||||||
znygAbOqAaFKqUXr7bkGY5ELm5lwGFECggEACcyaF9mMqLGghR55ew+cMmdeYdK3
|
|
||||||
meMLi5nrgtbQpVLlz+IV7Vdmrv7lZjeTr4nvU/5miU+p+If14CCFBiSucGq3Kmyp
|
|
||||||
OSM5cNCjDhw8uIDfY2qWCrZ2NSMR3qaAoBAQyQ2ER1IL98TDF/Qui0ZatbPiM4Ns
|
|
||||||
GErhkBZh3MCDSt24yiVKcUB79BxatWB4K7h7y8wqpX4Rj7rpfJMF7wz/I1cgyuCE
|
|
||||||
7XFpRwj7F1B2MmXnvV3KAgAD0EqrJDLeM0vIlDhpOUEaFUkuqmQyeB8qQkWfyXbD
|
|
||||||
jzloS3cNq0MBijB8oixwD2b4dVhBM7z8vQMX6OntN+97luWgO8OIukoYAg==
|
|
||||||
-----END RSA PRIVATE KEY-----
|
|
||||||
`)
|
|
||||||
|
|
||||||
publicKeyBytes := []byte(`-----BEGIN RSA PUBLIC KEY-----
|
secretKeyLocation := filepath.Join(testDir, secrets, contexts, qlikDefaultContext, secrets)
|
||||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEApFf3qCQhAr2QLRRZdhLy
|
if err := os.MkdirAll(secretKeyLocation, 0777); err != nil {
|
||||||
B8exLjrQiXLr8hwDe0xHSLJX3w7v5z4ujJWrHUulQ2/hvS8uffxMVrp2YqeA4sjy
|
|
||||||
/ku1KqZVQv/WNTdL2v9Z1ewbRnBjDQvmkWDKZ+cP8VPdHGzQ4iM2z6BZ4RQTkdQM
|
|
||||||
KqsVwUsLO9amI2TOny/M696eFRW0pk4+W3QZZRawT0HqJPvKKXKqoO2+62W54rOV
|
|
||||||
8glJi29Do06e4S6CZUl1hBUy0VlLtrLlRSOHTois0dF2a9f7+GGgU11MHO6w1k1N
|
|
||||||
esSlfZ0vnrrkW8WFLqewk+Jj+w1xeQnYHOeHjx6zi9f9DC96eSylSB3iJ71NmXcM
|
|
||||||
c0IEZ2LiQIqTL83BLOgMBCsK3FSlGMakQUR2GJ+M0I/selYkRMhid6eOmhlsTNMP
|
|
||||||
bpcTHxZ+ReIzS+5B1X0FZ7RIL+jSL9mFcxmD3dxurrrt/DkLpXcuWdi1s1bpVn3j
|
|
||||||
IQhU0+bgA6hT0k8Kj2f3Q9QnvkHSEff+XyLvLwQeSsSAcnM+1I7fNSPEo2cq0au6
|
|
||||||
ZtjHcirXmMminAQ6cKW1XrEvJBefHHibtjJjIM4bHH7MKRA5H3km/J4CCwI1VogS
|
|
||||||
TcE05Z6ypAFU2TCrnec9c9VXkRDP94h0GuoG8sdhmQudqvghr/8T7CV+sGRQbdeq
|
|
||||||
rXwanfnGPrjcMVIO/dSOxOUCAwEAAQ==
|
|
||||||
-----END RSA PUBLIC KEY-----
|
|
||||||
`)
|
|
||||||
|
|
||||||
targetFile := filepath.Join(testDir, "targetfile.yaml")
|
|
||||||
// tests/config.yaml exists
|
|
||||||
err := ioutil.WriteFile(targetFile, []byte(targetFileString), 0777)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error while creating file: %v", err)
|
|
||||||
return nil, nil, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
secretKeyPairDir := filepath.Join(testDir, secrets, contexts, qlikDefaultContext, secrets)
|
|
||||||
if err := os.MkdirAll(secretKeyPairDir, 0777); err != nil {
|
|
||||||
err = fmt.Errorf("Not able to create directories")
|
err = fmt.Errorf("Not able to create directories")
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
os.Setenv("QLIKSENSE_KEY_LOCATION", secretKeyPairDir)
|
os.Setenv("QLIKSENSE_KEY_LOCATION", secretKeyLocation)
|
||||||
|
|
||||||
privKeyFile := filepath.Join(secretKeyPairDir, "qliksensePriv")
|
//privKeyFile := filepath.Join(secretKeyLocation, "user_secret_key")
|
||||||
// construct and write priv key file into secretsDir location
|
key, err := api.LoadSecretKey(secretKeyLocation)
|
||||||
err = ioutil.WriteFile(privKeyFile, privKeyBytes, 0777)
|
if key == "" {
|
||||||
|
key, err = api.GenerateAndStoreSecretKey(secretKeyLocation)
|
||||||
|
}
|
||||||
|
encData, _ := api.EncryptData([]byte(decText), key)
|
||||||
|
encText := b64.StdEncoding.EncodeToString(encData)
|
||||||
|
|
||||||
|
targetFileString := fmt.Sprintf(targetFileStringTemplate, encText)
|
||||||
|
targetFile := filepath.Join(testDir, "targetfile.yaml")
|
||||||
|
// tests/config.yaml exists
|
||||||
|
err = ioutil.WriteFile(targetFile, []byte(targetFileString), 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error while creating file: %v", err)
|
log.Printf("Error while creating file: %v", err)
|
||||||
return nil, nil, "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
pubKeyFile := filepath.Join(secretKeyPairDir, "qliksensePub")
|
|
||||||
api.LogDebugMessage("Test setup - \npub key path: %s\n, priv key path: %s\n", pubKeyFile, privKeyFile)
|
|
||||||
// construct and write pub key file into secretsDir location
|
|
||||||
err = ioutil.WriteFile(pubKeyFile, publicKeyBytes, 0777)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error while creating file: %v", err)
|
|
||||||
return nil, nil, "", err
|
|
||||||
}
|
|
||||||
return publicKeyBytes, privKeyBytes, targetFile, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func removePrivateKey() {
|
return targetFile, key, err
|
||||||
err := os.Remove(filepath.Join(testDir, secrets, contexts, qlikDefaultContext, secrets, "qliksensePriv"))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Could not delete private key %v", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setup() func() {
|
func setup() func() {
|
||||||
@@ -224,7 +140,8 @@ func Test_retrieveCurrentContextInfo(t *testing.T) {
|
|||||||
q := &Qliksense{
|
q := &Qliksense{
|
||||||
QliksenseHome: testDir,
|
QliksenseHome: testDir,
|
||||||
}
|
}
|
||||||
_, _, err := retrieveCurrentContextInfo(q)
|
qConfig := api.NewQConfig(q.QliksenseHome)
|
||||||
|
_, err := qConfig.GetCurrentCR()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
@@ -327,7 +244,7 @@ func TestSetOtherConfigs(t *testing.T) {
|
|||||||
q: &Qliksense{
|
q: &Qliksense{
|
||||||
QliksenseHome: testDir,
|
QliksenseHome: testDir,
|
||||||
},
|
},
|
||||||
args: []string{"profile=minikube", "rotateKeys=yes", "storageClassName=efs", "gitops.enabled=yes", "gitops.schedule=30 * * * *"},
|
args: []string{"profile=minikube", "rotateKeys=yes", "storageClassName=efs", "gitOps.enabled=yes", "gitOps.schedule=30 * * * *", "git.repository=master", "git.userName=foo", "git.accessToken=1234"},
|
||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
@@ -337,7 +254,7 @@ func TestSetOtherConfigs(t *testing.T) {
|
|||||||
q: &Qliksense{
|
q: &Qliksense{
|
||||||
QliksenseHome: testDir,
|
QliksenseHome: testDir,
|
||||||
},
|
},
|
||||||
args: []string{"someconfig=somevalue, gitops.schedule=bar", "gitops.enabled=bar"},
|
args: []string{"someconfig=somevalue, gitOps.schedule=bar", "gitOps.enabled=bar", "git.foo=bar", "rotateKeys=bar"},
|
||||||
},
|
},
|
||||||
wantErr: true,
|
wantErr: true,
|
||||||
},
|
},
|
||||||
@@ -388,7 +305,7 @@ func TestSetConfigs(t *testing.T) {
|
|||||||
defer tearDown()
|
defer tearDown()
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
if err := tt.args.q.SetConfigs(tt.args.args); (err != nil) != tt.wantErr {
|
if err := tt.args.q.SetConfigs(tt.args.args, false); (err != nil) != tt.wantErr {
|
||||||
t.Errorf("SetConfigs() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("SetConfigs() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -507,9 +424,14 @@ spec:
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func removePrivateKey() {
|
||||||
|
err := os.Remove(filepath.Join(testDir, secrets, contexts, qlikDefaultContext, secrets, "user_secret_key"))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Could not delete private key %v", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
func Test_PrepareK8sSecret(t *testing.T) {
|
func Test_PrepareK8sSecret(t *testing.T) {
|
||||||
|
|
||||||
type fields struct {
|
type fields struct {
|
||||||
QliksenseHome string
|
QliksenseHome string
|
||||||
}
|
}
|
||||||
@@ -529,7 +451,7 @@ func Test_PrepareK8sSecret(t *testing.T) {
|
|||||||
wantErr: false,
|
wantErr: false,
|
||||||
setup: func() (string, func()) {
|
setup: func() (string, func()) {
|
||||||
tearDown := setup()
|
tearDown := setup()
|
||||||
_, _, targetFile, _ := setupTargetFileAndPrivateKey()
|
targetFile, _, _ := setupTargetFileAndPrivateKey()
|
||||||
return targetFile, tearDown
|
return targetFile, tearDown
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -542,7 +464,7 @@ func Test_PrepareK8sSecret(t *testing.T) {
|
|||||||
wantErr: true,
|
wantErr: true,
|
||||||
setup: func() (string, func()) {
|
setup: func() (string, func()) {
|
||||||
tearDown := setup()
|
tearDown := setup()
|
||||||
_, _, targetFile, _ := setupTargetFileAndPrivateKey()
|
targetFile, _, _ := setupTargetFileAndPrivateKey()
|
||||||
removePrivateKey()
|
removePrivateKey()
|
||||||
return targetFile, tearDown
|
return targetFile, tearDown
|
||||||
},
|
},
|
||||||
@@ -556,8 +478,7 @@ func Test_PrepareK8sSecret(t *testing.T) {
|
|||||||
wantErr: true,
|
wantErr: true,
|
||||||
setup: func() (string, func()) {
|
setup: func() (string, func()) {
|
||||||
tearDown := setup()
|
tearDown := setup()
|
||||||
_, _, _, _ = setupTargetFileAndPrivateKey()
|
setupTargetFileAndPrivateKey()
|
||||||
removePrivateKey()
|
|
||||||
return "", tearDown
|
return "", tearDown
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -637,6 +558,7 @@ func Test_SetSecrets(t *testing.T) {
|
|||||||
type args struct {
|
type args struct {
|
||||||
args []string
|
args []string
|
||||||
isSecretSet bool
|
isSecretSet bool
|
||||||
|
base64 bool
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -655,6 +577,18 @@ func Test_SetSecrets(t *testing.T) {
|
|||||||
},
|
},
|
||||||
wantErr: false,
|
wantErr: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "valid secret secrets=false base64 encoded",
|
||||||
|
fields: fields{
|
||||||
|
QliksenseHome: testDir,
|
||||||
|
},
|
||||||
|
args: args{
|
||||||
|
args: []string{"qliksense.mongoDbUri=bW9uZ29kYjovL3FsaWstZGVmYXVsdC1tb25nb2RiOjI3MDE3L3FsaWtzZW5zZT9zc2w9ZmFsc2U="},
|
||||||
|
isSecretSet: false,
|
||||||
|
base64: true,
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "test1 valid secret secrets=true",
|
name: "test1 valid secret secrets=true",
|
||||||
fields: fields{
|
fields: fields{
|
||||||
@@ -690,22 +624,17 @@ func Test_SetSecrets(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
tearDown := setup()
|
tearDown := setup()
|
||||||
_, privateKeyBytes, _, err := setupTargetFileAndPrivateKey()
|
_, encryptionKey, err := setupTargetFileAndPrivateKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
defer tearDown()
|
defer tearDown()
|
||||||
|
|
||||||
privKey, err := api.DecodeToPrivateKey(privateKeyBytes)
|
|
||||||
if err != nil {
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
q := &Qliksense{
|
q := &Qliksense{
|
||||||
QliksenseHome: tt.fields.QliksenseHome,
|
QliksenseHome: tt.fields.QliksenseHome,
|
||||||
}
|
}
|
||||||
if err := q.SetSecrets(tt.args.args, tt.args.isSecretSet); (err != nil) != tt.wantErr {
|
if err := q.SetSecrets(tt.args.args, tt.args.isSecretSet, tt.args.base64); (err != nil) != tt.wantErr {
|
||||||
t.Errorf("SetSecrets() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("SetSecrets() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
@@ -716,7 +645,10 @@ func Test_SetSecrets(t *testing.T) {
|
|||||||
// extract the value for testing
|
// extract the value for testing
|
||||||
testValueArr := strings.SplitN(tt.args.args[0], "=", 2)
|
testValueArr := strings.SplitN(tt.args.args[0], "=", 2)
|
||||||
testValue := strings.ReplaceAll(testValueArr[1], "\"", "")
|
testValue := strings.ReplaceAll(testValueArr[1], "\"", "")
|
||||||
|
if tt.args.base64 {
|
||||||
|
d, _ := b64.StdEncoding.DecodeString(testValue)
|
||||||
|
testValue = strings.Trim(string(d), "\n ")
|
||||||
|
}
|
||||||
qliksenseCR, err := readCRFile()
|
qliksenseCR, err := readCRFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("Not able to read from context file: %v", err)
|
err = fmt.Errorf("Not able to read from context file: %v", err)
|
||||||
@@ -733,13 +665,7 @@ func Test_SetSecrets(t *testing.T) {
|
|||||||
log.Printf("decode error: %v", err)
|
log.Printf("decode error: %v", err)
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
decodedValue, err := b64.StdEncoding.DecodeString(valToBeEncrypted)
|
decryptedVal, err := api.DecryptData([]byte(valToBeEncrypted), encryptionKey)
|
||||||
if err != nil {
|
|
||||||
err := fmt.Errorf("Error occurred while decoding: %v", err)
|
|
||||||
log.Printf("decode error: %v", err)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
decryptedVal, err := api.Decrypt(decodedValue, privKey)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("Error occurred while testing decryption: %v", err)
|
err := fmt.Errorf("Error occurred while testing decryption: %v", err)
|
||||||
log.Printf("No Data in Secret: %v", err)
|
log.Printf("No Data in Secret: %v", err)
|
||||||
@@ -780,7 +706,8 @@ func getValueToBeDecodedForSetSecrets(item config.NameValue, qliksenseCR *api.Ql
|
|||||||
}
|
}
|
||||||
// secret=false
|
// secret=false
|
||||||
if item.Value != "" {
|
if item.Value != "" {
|
||||||
return item.Value, nil
|
b, err := b64.RawStdEncoding.DecodeString(item.Value)
|
||||||
|
return string(b), err
|
||||||
}
|
}
|
||||||
err := fmt.Errorf("Both Value and ValueFrom are empty")
|
err := fmt.Errorf("Both Value and ValueFrom are empty")
|
||||||
return "", err
|
return "", err
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package qliksense
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
@@ -19,12 +20,24 @@ func (q *Qliksense) ViewCrds(opts *CrdCommandOptions) error {
|
|||||||
fmt.Println("cannot get the current-context cr", err)
|
fmt.Println("cannot get the current-context cr", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if engineCRD, err := getQliksenseInitCrd(qcr); err != nil {
|
engineCRD, err := getQliksenseInitCrd(qcr)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
} else if opts.All {
|
}
|
||||||
fmt.Printf("%s\n%s", q.GetOperatorCRDString(), engineCRD)
|
customCrd, err := getCustomCrd(qcr)
|
||||||
} else {
|
if err != nil {
|
||||||
fmt.Printf("%s", engineCRD)
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(engineCRD)
|
||||||
|
if customCrd != "" {
|
||||||
|
fmt.Println("---")
|
||||||
|
fmt.Println(customCrd)
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.All {
|
||||||
|
fmt.Println("---")
|
||||||
|
fmt.Printf("%s", q.GetOperatorCRDString())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -43,6 +56,14 @@ func (q *Qliksense) InstallCrds(opts *CrdCommandOptions) error {
|
|||||||
} else if err = qapi.KubectlApply(engineCRD, ""); err != nil {
|
} else if err = qapi.KubectlApply(engineCRD, ""); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if customCrd, err := getCustomCrd(qcr); err != nil {
|
||||||
|
return err
|
||||||
|
} else if customCrd != "" {
|
||||||
|
if err = qapi.KubectlApply(customCrd, ""); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if opts.All { // install opeartor crd
|
if opts.All { // install opeartor crd
|
||||||
if err := qapi.KubectlApply(q.GetOperatorCRDString(), ""); err != nil {
|
if err := qapi.KubectlApply(q.GetOperatorCRDString(), ""); err != nil {
|
||||||
fmt.Println("cannot do kubectl apply on opeartor CRD", err)
|
fmt.Println("cannot do kubectl apply on opeartor CRD", err)
|
||||||
@@ -59,16 +80,33 @@ func getQliksenseInitCrd(qcr *qapi.QliksenseCR) (string, error) {
|
|||||||
if qcr.Spec.GetManifestsRoot() != "" {
|
if qcr.Spec.GetManifestsRoot() != "" {
|
||||||
repoPath = qcr.Spec.GetManifestsRoot()
|
repoPath = qcr.Spec.GetManifestsRoot()
|
||||||
} else {
|
} else {
|
||||||
if repoPath, err = downloadFromGitRepoToTmpDir(defaultConfigRepoGitUrl, "master"); err != nil {
|
if repoPath, err = DownloadFromGitRepoToTmpDir(defaultConfigRepoGitUrl, "master"); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qInitMsPath := filepath.Join(repoPath, Q_INIT_CRD_PATH)
|
qInitMsPath := filepath.Join(repoPath, Q_INIT_CRD_PATH)
|
||||||
qInitByte, err := executeKustomizeBuild(qInitMsPath)
|
if _, err := os.Lstat(qInitMsPath); err != nil {
|
||||||
|
// older version of qliksense-init used
|
||||||
|
qInitMsPath = filepath.Join(repoPath, "manifests/base/manifests/qliksense-init")
|
||||||
|
}
|
||||||
|
qInitByte, err := ExecuteKustomizeBuild(qInitMsPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("cannot generate crds for qliksense-init", err)
|
fmt.Println("cannot generate crds for qliksense-init", err)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return string(qInitByte), nil
|
return string(qInitByte), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getCustomCrd(qcr *qapi.QliksenseCR) (string, error) {
|
||||||
|
crdPath := qcr.GetCustomCrdsPath()
|
||||||
|
if crdPath == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
qInitByte, err := ExecuteKustomizeBuild(crdPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("cannot generate custom crds", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(qInitByte), nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestGetQliksenseInitCrd(t *testing.T) {
|
func TestGetQliksenseInitCrd(t *testing.T) {
|
||||||
someTmpRepoPath, err := downloadFromGitRepoToTmpDir(defaultConfigRepoGitUrl, "master")
|
someTmpRepoPath, err := DownloadFromGitRepoToTmpDir(defaultConfigRepoGitUrl, "master")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package qliksense
|
package qliksense
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
@@ -27,6 +28,31 @@ const (
|
|||||||
imageSharedBlobsDirName = "blobs"
|
imageSharedBlobsDirName = "blobs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (q *Qliksense) PullImages(version, profile string) error {
|
||||||
|
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||||
|
if version != "" {
|
||||||
|
if !qConfig.IsRepoExistForCurrent(version) {
|
||||||
|
if err := q.FetchQK8s(version); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qcr, err := qConfig.GetCurrentCR()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !qcr.IsRepoExist() {
|
||||||
|
return errors.New("ManifestsRoot not found")
|
||||||
|
}
|
||||||
|
if profile != "" {
|
||||||
|
qcr.Spec.Profile = profile
|
||||||
|
if e := qConfig.WriteCR(qcr); e != nil {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return q.PullImagesForCurrentCR()
|
||||||
|
}
|
||||||
|
|
||||||
// PullImages ...
|
// PullImages ...
|
||||||
func (q *Qliksense) PullImagesForCurrentCR() error {
|
func (q *Qliksense) PullImagesForCurrentCR() error {
|
||||||
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||||
@@ -49,7 +75,7 @@ func (q *Qliksense) PullImagesForCurrentCR() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
images := versionOut.Images
|
images := versionOut.Images
|
||||||
if err := q.appendOperatorImages(&images); err != nil {
|
if err := q.appendAdditionalImages(&images, qcr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,6 +95,19 @@ func (q *Qliksense) PullImagesForCurrentCR() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *Qliksense) appendGitOpsImage(images *[]string, qcr *qapi.QliksenseCR) {
|
||||||
|
if qcr.Spec.GitOps != nil && qcr.Spec.GitOps.Image != "" {
|
||||||
|
*images = append(*images, qcr.Spec.GitOps.Image)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Qliksense) appendPreflightImages(images *[]string) {
|
||||||
|
pf := qapi.NewPreflightConfig(q.QliksenseHome)
|
||||||
|
for _, preflightImage := range pf.GetImageMap() {
|
||||||
|
*images = append(*images, preflightImage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (q *Qliksense) appendOperatorImages(images *[]string) error {
|
func (q *Qliksense) appendOperatorImages(images *[]string) error {
|
||||||
if operatorImages, err := getImageList([]byte(q.GetOperatorControllerString())); err != nil {
|
if operatorImages, err := getImageList([]byte(q.GetOperatorControllerString())); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -131,7 +170,7 @@ func (q *Qliksense) PushImagesForCurrentCR() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
dockerConfigJsonSecret = &qapi.DockerConfigJsonSecret{
|
dockerConfigJsonSecret = &qapi.DockerConfigJsonSecret{
|
||||||
Uri: qcr.GetImageRegistry(),
|
Uri: qcr.Spec.GetImageRegistry(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return err
|
return err
|
||||||
@@ -149,7 +188,7 @@ func (q *Qliksense) PushImagesForCurrentCR() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
images := versionOut.Images
|
images := versionOut.Images
|
||||||
if err := q.appendOperatorImages(&images); err != nil {
|
if err := q.appendAdditionalImages(&images, qcr); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,6 +209,15 @@ func (q *Qliksense) PushImagesForCurrentCR() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *Qliksense) appendAdditionalImages(images *[]string, qcr *qapi.QliksenseCR) error {
|
||||||
|
if err := q.appendOperatorImages(images); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
q.appendGitOpsImage(images, qcr)
|
||||||
|
q.appendPreflightImages(images)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func pushImage(image, imagesDir string, dockerConfigJsonSecret *qapi.DockerConfigJsonSecret) error {
|
func pushImage(image, imagesDir string, dockerConfigJsonSecret *qapi.DockerConfigJsonSecret) error {
|
||||||
imageNameParts := getImageNameParts(image)
|
imageNameParts := getImageNameParts(image)
|
||||||
srcDir := filepath.Join(imagesDir, imageIndexDirName, imageNameParts.name, imageNameParts.tag)
|
srcDir := filepath.Join(imagesDir, imageIndexDirName, imageNameParts.name, imageNameParts.tag)
|
||||||
|
|||||||
@@ -68,6 +68,9 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Test_Pull_Push_ImagesForCurrentCR(t *testing.T) {
|
func Test_Pull_Push_ImagesForCurrentCR(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("Skipping pull/push tests in short mode")
|
||||||
|
}
|
||||||
var testCases = []struct {
|
var testCases = []struct {
|
||||||
name string
|
name string
|
||||||
registryAuth bool
|
registryAuth bool
|
||||||
@@ -131,7 +134,7 @@ func Test_Pull_Push_ImagesForCurrentCR(t *testing.T) {
|
|||||||
}
|
}
|
||||||
q := &Qliksense{
|
q := &Qliksense{
|
||||||
QliksenseHome: tmpQlikSenseHome,
|
QliksenseHome: tmpQlikSenseHome,
|
||||||
CrdBox: packr.New("crds", "./crds"),
|
CrdBox: &packr.Box{},
|
||||||
}
|
}
|
||||||
var versionOut VersionOutput
|
var versionOut VersionOutput
|
||||||
|
|
||||||
@@ -170,29 +173,88 @@ func Test_Pull_Push_ImagesForCurrentCR(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupQlikSenseHome(t *testing.T, tmpQlikSenseHome string, registry *testRegistryV2, clientAuth clientAuthType) error {
|
func Test_appendAdditionalImages(t *testing.T) {
|
||||||
if err := ioutil.WriteFile(path.Join(tmpQlikSenseHome, "config.yaml"), []byte(`
|
tmpQlikSenseHome, err := ioutil.TempDir("", "tmp-qlik-sense-home-")
|
||||||
apiVersion: config.qlik.com/v1
|
if err != nil {
|
||||||
kind: QliksenseConfig
|
t.Fatalf("unexpected error creating tmp dir: %v", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpQlikSenseHome)
|
||||||
|
|
||||||
|
setupQliksenseTestDefaultContext(t, tmpQlikSenseHome, `
|
||||||
|
apiVersion: qlik.com/v1
|
||||||
|
kind: Qliksense
|
||||||
metadata:
|
metadata:
|
||||||
name: QliksenseConfigMetadata
|
name: qlik-default
|
||||||
spec:
|
spec:
|
||||||
contexts:
|
gitOps:
|
||||||
- name: qlik-default
|
image: some-gitops-image
|
||||||
crFile: contexts/qlik-default/qlik-default.yaml
|
`)
|
||||||
currentContext: qlik-default
|
|
||||||
`), os.ModePerm); err != nil {
|
q := &Qliksense{
|
||||||
return err
|
QliksenseHome: tmpQlikSenseHome,
|
||||||
|
CrdBox: packr.New("crds", "./crds"),
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultContextDir := path.Join(tmpQlikSenseHome, "contexts", "qlik-default")
|
pf := api.NewPreflightConfig(q.QliksenseHome)
|
||||||
if err := os.MkdirAll(defaultContextDir, os.ModePerm); err != nil {
|
if err := pf.Initialize(); err != nil {
|
||||||
return err
|
t.Fatalf("unexpected error initializing preflight: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qConfig := api.NewQConfig(q.QliksenseHome)
|
||||||
|
qcr, err := qConfig.GetCurrentCR()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error getting current CR: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
images := make([]string, 0)
|
||||||
|
if err := q.appendAdditionalImages(&images, qcr); err != nil {
|
||||||
|
t.Fatalf("unexpected error appending additional images: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedNumberAdditionalImages := 5
|
||||||
|
if len(images) != expectedNumberAdditionalImages {
|
||||||
|
t.Fatalf("unexpected number of additional images: %v, expected: %v", len(images), expectedNumberAdditionalImages)
|
||||||
|
}
|
||||||
|
|
||||||
|
haveMatchingImage := func(test func(string) bool) bool {
|
||||||
|
for _, image := range images {
|
||||||
|
if test(image) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !haveMatchingImage(func(image string) bool {
|
||||||
|
return strings.Contains(image, "qlik-docker-oss.bintray.io/qliksense-operator:v")
|
||||||
|
}) {
|
||||||
|
t.Fatal("expected to find the operator image in the list, but it wasn't there")
|
||||||
|
}
|
||||||
|
if !haveMatchingImage(func(image string) bool {
|
||||||
|
return image == "some-gitops-image"
|
||||||
|
}) {
|
||||||
|
t.Fatal("expected to find the GitOps image in the list, but it wasn't there")
|
||||||
|
}
|
||||||
|
if !haveMatchingImage(func(image string) bool {
|
||||||
|
return image == "nginx"
|
||||||
|
}) {
|
||||||
|
t.Fatal("expected to find the nginx Preflight image in the list, but it wasn't there")
|
||||||
|
}
|
||||||
|
if !haveMatchingImage(func(image string) bool {
|
||||||
|
return image == "subfuzion/netcat"
|
||||||
|
}) {
|
||||||
|
t.Fatal("expected to find the netcat Preflight image in the list, but it wasn't there")
|
||||||
|
}
|
||||||
|
if !haveMatchingImage(func(image string) bool {
|
||||||
|
return image == "mongo"
|
||||||
|
}) {
|
||||||
|
t.Fatal("expected to find the mongo Preflight image in the list, but it wasn't there")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupQlikSenseHome(t *testing.T, tmpQlikSenseHome string, registry *testRegistryV2, clientAuth clientAuthType) error {
|
||||||
version := "foo"
|
version := "foo"
|
||||||
manifestsRootDir := fmt.Sprintf("%s/repo/%s", defaultContextDir, version)
|
manifestsRootDir := filepath.ToSlash(path.Join(tmpQlikSenseHome, "contexts", "qlik-default", "repo", version))
|
||||||
if err := ioutil.WriteFile(path.Join(defaultContextDir, "qlik-default.yaml"), []byte(fmt.Sprintf(`
|
cr := fmt.Sprintf(`
|
||||||
apiVersion: qlik.com/v1
|
apiVersion: qlik.com/v1
|
||||||
kind: Qliksense
|
kind: Qliksense
|
||||||
metadata:
|
metadata:
|
||||||
@@ -208,9 +270,8 @@ spec:
|
|||||||
manifestsRoot: %s
|
manifestsRoot: %s
|
||||||
rotateKeys: "yes"
|
rotateKeys: "yes"
|
||||||
releaseName: qlik-default
|
releaseName: qlik-default
|
||||||
`, version, registry.url, manifestsRootDir)), os.ModePerm); err != nil {
|
`, version, registry.url, manifestsRootDir)
|
||||||
return err
|
setupQliksenseTestDefaultContext(t, tmpQlikSenseHome, cr)
|
||||||
}
|
|
||||||
|
|
||||||
if clientAuth == clientAuthProvided || clientAuth == clientAuthProvidedButIncorrect {
|
if clientAuth == clientAuthProvided || clientAuth == clientAuthProvidedButIncorrect {
|
||||||
if registry.username == "" || clientAuth == clientAuthProvidedButIncorrect {
|
if registry.username == "" || clientAuth == clientAuthProvidedButIncorrect {
|
||||||
|
|||||||
@@ -1,13 +1,28 @@
|
|||||||
package qliksense
|
package qliksense
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/go-git/go-git/v5/plumbing/transport"
|
||||||
|
"github.com/go-git/go-git/v5/plumbing/transport/http"
|
||||||
kapis_git "github.com/qlik-oss/k-apis/pkg/git"
|
kapis_git "github.com/qlik-oss/k-apis/pkg/git"
|
||||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type FetchCommandOptions struct {
|
||||||
|
GitUrl string
|
||||||
|
AccessToken string
|
||||||
|
Version string
|
||||||
|
SecretName string
|
||||||
|
Overwrite bool
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
QLIK_GIT_REPO = "https://github.com/qlik-oss/qliksense-k8s"
|
QLIK_GIT_REPO = "https://github.com/qlik-oss/qliksense-k8s"
|
||||||
)
|
)
|
||||||
@@ -17,24 +32,125 @@ func (q *Qliksense) FetchQK8s(version string) error {
|
|||||||
return fetchAndUpdateCR(qConfig, version)
|
return fetchAndUpdateCR(qConfig, version)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *Qliksense) FetchK8sWithOpts(opts *FetchCommandOptions) error {
|
||||||
|
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||||
|
cr, err := qConfig.GetCurrentCR()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if opts.AccessToken != "" {
|
||||||
|
encKey, err := qConfig.GetEncryptionKeyFor(cr.GetName())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := cr.SetFetchAccessToken(opts.AccessToken, encKey); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if opts.SecretName != "" {
|
||||||
|
cr.SetFetchAccessSecretName(opts.SecretName)
|
||||||
|
}
|
||||||
|
if opts.GitUrl != "" {
|
||||||
|
cr.SetFetchUrl(opts.GitUrl)
|
||||||
|
}
|
||||||
|
v := getVersion(opts, cr)
|
||||||
|
if v == "" {
|
||||||
|
return errors.New("Cannot find gitref/tag/branch/version to fetch")
|
||||||
|
}
|
||||||
|
if qConfig.IsRepoExistForCurrent(v) {
|
||||||
|
if opts.Overwrite || getVerionsOverwriteConfirmation(v) == "y" {
|
||||||
|
if err := qConfig.DeleteRepoForCurrent(v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// nothing to do
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qConfig.WriteCR(cr)
|
||||||
|
return fetchAndUpdateCR(qConfig, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetchAndUpdateCR fetch
|
||||||
func fetchAndUpdateCR(qConfig *qapi.QliksenseConfig, version string) error {
|
func fetchAndUpdateCR(qConfig *qapi.QliksenseConfig, version string) error {
|
||||||
qcr, err := qConfig.GetCurrentCR()
|
qcr, err := qConfig.GetCurrentCR()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("cannot get the current-context cr", err)
|
fmt.Println("cannot get the current-context cr", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if qConfig.IsRepoExistForCurrent(version) {
|
if version == "" {
|
||||||
return nil
|
if qcr.GetLabelFromCr("version") == "" {
|
||||||
|
return errors.New("Cannot find gitref/tag/branch/version to fetch")
|
||||||
|
}
|
||||||
|
version = qcr.GetLabelFromCr("version")
|
||||||
|
}
|
||||||
|
encKey, err := qConfig.GetEncryptionKeyFor(qcr.GetName())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// downlaod to temp first
|
||||||
|
tempDest, err := fetchToTempDir(qcr.GetFetchUrl(), version, qcr.GetFetchAccessToken(encKey))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
destDir := qConfig.BuildRepoPath(version)
|
|
||||||
fmt.Printf("fetching version [%s] from %s\n", version, QLIK_GIT_REPO)
|
|
||||||
|
|
||||||
if repo, err := kapis_git.CloneRepository(destDir, QLIK_GIT_REPO, nil); err != nil {
|
destDir := qConfig.BuildRepoPath(version)
|
||||||
return err
|
fmt.Printf("fetching version [%s] from %s\n", version, qcr.GetFetchUrl())
|
||||||
} else if err = kapis_git.Checkout(repo, version, fmt.Sprintf("%v-by-operator-%v", version, uuid.New().String()), nil); err != nil {
|
if err := qapi.CopyDirectory(tempDest, destDir); err != nil {
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
qcr.Spec.ManifestsRoot = qConfig.BuildCurrentManifestsRoot(version)
|
qcr.Spec.ManifestsRoot = qConfig.BuildCurrentManifestsRoot(version)
|
||||||
qcr.AddLabelToCr("version", version)
|
qcr.AddLabelToCr("version", version)
|
||||||
return qConfig.WriteCurrentContextCR(qcr)
|
return qConfig.WriteCurrentContextCR(qcr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fetchToTempDir(gitUrl, gitRef, accessToken string) (string, error) {
|
||||||
|
tmpDir, err := ioutil.TempDir("", "")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
downloadPath := path.Join(tmpDir, "repo")
|
||||||
|
var auth transport.AuthMethod
|
||||||
|
if accessToken != "" {
|
||||||
|
auth = &http.BasicAuth{
|
||||||
|
Username: "something",
|
||||||
|
Password: accessToken,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if repo, err := kapis_git.CloneRepository(downloadPath, gitUrl, auth); err != nil {
|
||||||
|
return "", err
|
||||||
|
} else if err := kapis_git.Checkout(repo, gitRef, "", auth); err != nil {
|
||||||
|
return "", err
|
||||||
|
} else {
|
||||||
|
return downloadPath, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getVersion(opts *FetchCommandOptions, qcr *qapi.QliksenseCR) string {
|
||||||
|
if opts.Version == "" {
|
||||||
|
if qcr.GetLabelFromCr("version") != "" {
|
||||||
|
return qcr.GetLabelFromCr("version")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return opts.Version
|
||||||
|
}
|
||||||
|
|
||||||
|
func getVerionsOverwriteConfirmation(version string) string {
|
||||||
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
fmt.Println("The version [" + version + "] already exist")
|
||||||
|
cfm := "n"
|
||||||
|
for {
|
||||||
|
fmt.Print("Do you want to delete and fetch again [y/N]: ")
|
||||||
|
cfm, _ = reader.ReadString('\n')
|
||||||
|
cfm = strings.Replace(cfm, "\n", "", -1)
|
||||||
|
cfm = strings.TrimSpace(cfm)
|
||||||
|
if cfm == "" {
|
||||||
|
cfm = "n"
|
||||||
|
}
|
||||||
|
cfm = strings.ToLower(cfm)
|
||||||
|
if cfm == "y" || cfm == "n" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cfm
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ func (q *Qliksense) GetInstallableVersions(opts *LsRemoteCmdOptions) error {
|
|||||||
if qcr.Spec.GetManifestsRoot() != "" {
|
if qcr.Spec.GetManifestsRoot() != "" {
|
||||||
repoPath = qcr.Spec.GetManifestsRoot()
|
repoPath = qcr.Spec.GetManifestsRoot()
|
||||||
} else {
|
} else {
|
||||||
repoPath, err = downloadFromGitRepoToTmpDir(defaultConfigRepoGitUrl, "master")
|
repoPath, err = DownloadFromGitRepoToTmpDir(defaultConfigRepoGitUrl, "master")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package qliksense
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/qlik-oss/k-apis/pkg/config"
|
"github.com/qlik-oss/k-apis/pkg/config"
|
||||||
@@ -27,11 +28,9 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, kee
|
|||||||
// fetch the version
|
// fetch the version
|
||||||
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||||
if !keepPatchFiles {
|
if !keepPatchFiles {
|
||||||
defer func() {
|
if err := q.DiscardAllUnstagedChangesFromGitRepo(qConfig); err != nil {
|
||||||
if err := q.DiscardAllUnstagedChangesFromGitRepo(qConfig); err != nil {
|
fmt.Printf("error removing temporary changes to the config: %v\n", err)
|
||||||
fmt.Printf("error removing temporary changes to the config: %v\n", err)
|
}
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qcr, err := qConfig.GetCurrentCR()
|
qcr, err := qConfig.GetCurrentCR()
|
||||||
@@ -66,16 +65,11 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, kee
|
|||||||
//CRD will be installed outside of operator
|
//CRD will be installed outside of operator
|
||||||
//install operator controller into the namespace
|
//install operator controller into the namespace
|
||||||
fmt.Println("Installing operator controller")
|
fmt.Println("Installing operator controller")
|
||||||
operatorControllerString := q.GetOperatorControllerString()
|
if operatorControllerString, err := q.getProcessedOperatorControllerString(qcr); err != nil {
|
||||||
if imageRegistry := qcr.GetImageRegistry(); imageRegistry != "" {
|
fmt.Println("error extracting/transforming operator controller", err)
|
||||||
operatorControllerString, err = kustomizeForImageRegistry(operatorControllerString, pullSecretName,
|
return err
|
||||||
"qlik/qliksense-operator", fmt.Sprintf("%v/qliksense-operator", imageRegistry))
|
} else if err := qapi.KubectlApply(operatorControllerString, ""); err != nil {
|
||||||
if err != nil {
|
fmt.Println("cannot do kubectl apply on operator controller", err)
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := qapi.KubectlApply(operatorControllerString, ""); err != nil {
|
|
||||||
fmt.Println("cannot do kubectl apply on opeartor controller", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +88,7 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, kee
|
|||||||
return q.applyCR(dcr)
|
return q.applyCR(dcr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if version != "" { // no need to fetch manifest root already set by some other way
|
if !qcr.IsRepoExist() {
|
||||||
if err := fetchAndUpdateCR(qConfig, version); err != nil {
|
if err := fetchAndUpdateCR(qConfig, version); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -121,9 +115,19 @@ func (q *Qliksense) InstallQK8s(version string, opts *InstallCommandOptions, kee
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *Qliksense) getProcessedOperatorControllerString(qcr *qapi.QliksenseCR) (string, error) {
|
||||||
|
operatorControllerString := q.GetOperatorControllerString()
|
||||||
|
if imageRegistry := qcr.Spec.GetImageRegistry(); imageRegistry != "" {
|
||||||
|
return kustomizeForImageRegistry(operatorControllerString, pullSecretName,
|
||||||
|
path.Join(qliksenseOperatorImageRepo, qliksenseOperatorImageName),
|
||||||
|
path.Join(imageRegistry, qliksenseOperatorImageName))
|
||||||
|
}
|
||||||
|
return operatorControllerString, nil
|
||||||
|
}
|
||||||
|
|
||||||
func installOrRemoveImagePullSecret(qConfig *qapi.QliksenseConfig) error {
|
func installOrRemoveImagePullSecret(qConfig *qapi.QliksenseConfig) error {
|
||||||
if pullDockerConfigJsonSecret, err := qConfig.GetPullDockerConfigJsonSecret(); err == nil {
|
if pullDockerConfigJsonSecret, err := qConfig.GetPullDockerConfigJsonSecret(); err == nil {
|
||||||
if dockerConfigJsonSecretYaml, err := pullDockerConfigJsonSecret.ToYaml(nil); err != nil {
|
if dockerConfigJsonSecretYaml, err := pullDockerConfigJsonSecret.ToYaml(""); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if err := qapi.KubectlApply(string(dockerConfigJsonSecretYaml), ""); err != nil {
|
} else if err := qapi.KubectlApply(string(dockerConfigJsonSecretYaml), ""); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -132,7 +136,7 @@ func installOrRemoveImagePullSecret(qConfig *qapi.QliksenseConfig) error {
|
|||||||
deleteDockerConfigJsonSecret := qapi.DockerConfigJsonSecret{
|
deleteDockerConfigJsonSecret := qapi.DockerConfigJsonSecret{
|
||||||
Name: pullSecretName,
|
Name: pullSecretName,
|
||||||
}
|
}
|
||||||
if deleteDockerConfigJsonSecretYaml, err := deleteDockerConfigJsonSecret.ToYaml(nil); err != nil {
|
if deleteDockerConfigJsonSecretYaml, err := deleteDockerConfigJsonSecret.ToYaml(""); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if err := qapi.KubectlDelete(string(deleteDockerConfigJsonSecretYaml), ""); err != nil {
|
} else if err := qapi.KubectlDelete(string(deleteDockerConfigJsonSecretYaml), ""); err != nil {
|
||||||
qapi.LogDebugMessage("failed deleting %v, error: %v\n", pullSecretName, err)
|
qapi.LogDebugMessage("failed deleting %v, error: %v\n", pullSecretName, err)
|
||||||
|
|||||||
@@ -1,11 +1,20 @@
|
|||||||
package qliksense
|
package qliksense
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"sigs.k8s.io/kustomize/api/k8sdeps/kunstruct"
|
||||||
|
"sigs.k8s.io/kustomize/api/resid"
|
||||||
|
"sigs.k8s.io/kustomize/api/resmap"
|
||||||
|
"sigs.k8s.io/kustomize/api/resource"
|
||||||
|
|
||||||
|
"github.com/gobuffalo/packr/v2"
|
||||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -63,3 +72,106 @@ spec:
|
|||||||
}
|
}
|
||||||
td()
|
td()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupQliksenseTestDefaultContext(t *testing.T, tmpQlikSenseHome, CR string) {
|
||||||
|
if err := ioutil.WriteFile(path.Join(tmpQlikSenseHome, "config.yaml"), []byte(`
|
||||||
|
apiVersion: config.qlik.com/v1
|
||||||
|
kind: QliksenseConfig
|
||||||
|
metadata:
|
||||||
|
name: QliksenseConfigMetadata
|
||||||
|
spec:
|
||||||
|
contexts:
|
||||||
|
- name: qlik-default
|
||||||
|
crFile: contexts/qlik-default/qlik-default.yaml
|
||||||
|
currentContext: qlik-default
|
||||||
|
`), os.ModePerm); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultContextDir := path.Join(tmpQlikSenseHome, "contexts", "qlik-default")
|
||||||
|
if err := os.MkdirAll(defaultContextDir, os.ModePerm); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ioutil.WriteFile(path.Join(defaultContextDir, "qlik-default.yaml"), []byte(CR), os.ModePerm); err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_getProcessedOperatorControllerString(t *testing.T) {
|
||||||
|
tmpQlikSenseHome, err := ioutil.TempDir("", "tmp-qlik-sense-home-")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error creating tmp dir: %v", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpQlikSenseHome)
|
||||||
|
|
||||||
|
registry := "registryFoo"
|
||||||
|
setupQliksenseTestDefaultContext(t, tmpQlikSenseHome, fmt.Sprintf(`
|
||||||
|
apiVersion: qlik.com/v1
|
||||||
|
kind: Qliksense
|
||||||
|
metadata:
|
||||||
|
name: qlik-default
|
||||||
|
spec:
|
||||||
|
configs:
|
||||||
|
qliksense:
|
||||||
|
- name: imageRegistry
|
||||||
|
value: %v
|
||||||
|
`, registry))
|
||||||
|
|
||||||
|
q := &Qliksense{
|
||||||
|
QliksenseHome: tmpQlikSenseHome,
|
||||||
|
CrdBox: packr.New("crds", "./crds"),
|
||||||
|
}
|
||||||
|
|
||||||
|
qConfig := qapi.NewQConfig(q.QliksenseHome)
|
||||||
|
qcr, err := qConfig.GetCurrentCR()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error getting current CR: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
originalOperatorString := q.GetOperatorControllerString()
|
||||||
|
|
||||||
|
processedOperatorString, err := q.getProcessedOperatorControllerString(qcr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
controllerImageChecks := map[string]func(t *testing.T, controllerImage string){
|
||||||
|
originalOperatorString: func(t *testing.T, controllerImage string) {
|
||||||
|
expectedControllerImagePrefix := fmt.Sprintf("%v/%v:", qliksenseOperatorImageRepo, qliksenseOperatorImageName)
|
||||||
|
if !strings.HasPrefix(controllerImage, expectedControllerImagePrefix) {
|
||||||
|
t.Fatalf("expected controller image: %v to have prefix: %v", controllerImage, expectedControllerImagePrefix)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
processedOperatorString: func(t *testing.T, controllerImage string) {
|
||||||
|
expectedControllerImagePrefix := fmt.Sprintf("%v/%v:", registry, qliksenseOperatorImageName)
|
||||||
|
if !strings.HasPrefix(controllerImage, expectedControllerImagePrefix) {
|
||||||
|
t.Fatalf("expected controller image: %v to have prefix: %v", controllerImage, expectedControllerImagePrefix)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceFactory := resmap.NewFactory(resource.NewFactory(kunstruct.NewKunstructuredFactoryImpl()), nil)
|
||||||
|
for operatorString, controllerImageCheck := range controllerImageChecks {
|
||||||
|
resMap, err := resourceFactory.NewResMapFromBytes([]byte(operatorString))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := resMap.GetById(resid.NewResId(resid.Gvk{
|
||||||
|
Group: "apps",
|
||||||
|
Version: "v1",
|
||||||
|
Kind: "Deployment",
|
||||||
|
}, "qliksense-operator"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
controllerImage, err := res.GetString("spec.template.spec.containers[0].image")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
controllerImageCheck(t, controllerImage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
package qliksense
|
package qliksense
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/qlik-oss/sense-installer/pkg/api"
|
"github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
|
|
||||||
@@ -13,7 +15,8 @@ import (
|
|||||||
"sigs.k8s.io/kustomize/api/types"
|
"sigs.k8s.io/kustomize/api/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func executeKustomizeBuild(directory string) ([]byte, error) {
|
//ExecuteKustomizeBuild execute kustomize to the directory and return manifest as byte array
|
||||||
|
func ExecuteKustomizeBuild(directory string) ([]byte, error) {
|
||||||
return executeKustomizeBuildForFileSystem(directory, filesys.MakeFsOnDisk())
|
return executeKustomizeBuildForFileSystem(directory, filesys.MakeFsOnDisk())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,10 +42,26 @@ func executeKustomizeBuildForFileSystem(directory string, fSys filesys.FileSyste
|
|||||||
|
|
||||||
func executeKustomizeBuildWithStdoutProgress(path string) (kuzManifest []byte, err error) {
|
func executeKustomizeBuildWithStdoutProgress(path string) (kuzManifest []byte, err error) {
|
||||||
result, err := api.ExecuteTaskWithBlinkingStdoutFeedback(func() (interface{}, error) {
|
result, err := api.ExecuteTaskWithBlinkingStdoutFeedback(func() (interface{}, error) {
|
||||||
return executeKustomizeBuild(path)
|
return ExecuteKustomizeBuild(path)
|
||||||
}, "...")
|
}, "...")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return result.([]byte), nil
|
return result.([]byte), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//GetYamlsFromMultiDoc filter yaml docs from multiyaml based on kind
|
||||||
|
func GetYamlsFromMultiDoc(multiYaml string, kind string) string {
|
||||||
|
yamlDocs := strings.Split(string(multiYaml), "---")
|
||||||
|
resultDocs := ""
|
||||||
|
for _, doc := range yamlDocs {
|
||||||
|
scanner := bufio.NewScanner(strings.NewReader(doc))
|
||||||
|
for scanner.Scan() {
|
||||||
|
if scanner.Text() == "kind: "+kind {
|
||||||
|
resultDocs = resultDocs + "\n---\n" + doc
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultDocs
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
kapis_git "github.com/qlik-oss/k-apis/pkg/git"
|
kapis_git "github.com/qlik-oss/k-apis/pkg/git"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_executeKustomizeBuild(t *testing.T) {
|
func Test_ExecuteKustomizeBuild(t *testing.T) {
|
||||||
tmpDir, err := ioutil.TempDir("", "")
|
tmpDir, err := ioutil.TempDir("", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v\n", err)
|
t.Fatalf("unexpected error: %v\n", err)
|
||||||
@@ -41,7 +41,7 @@ configMapGenerator:
|
|||||||
t.Fatalf("error writing kustomization file to path: %v error: %v\n", kustomizationYamlFilePath, err)
|
t.Fatalf("error writing kustomization file to path: %v error: %v\n", kustomizationYamlFilePath, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := executeKustomizeBuild(tmpDir)
|
result, err := ExecuteKustomizeBuild(tmpDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected kustomize error: %v\n", err)
|
t.Fatalf("unexpected kustomize error: %v\n", err)
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,7 @@ func Test_executeKustomizeBuild_onQlikConfig_regenerateKeys(t *testing.T) {
|
|||||||
|
|
||||||
generateKeys(cr, "won't-use")
|
generateKeys(cr, "won't-use")
|
||||||
|
|
||||||
yamlResources, err := executeKustomizeBuild(path.Join(configPath, "manifests", "base", "resources", "users"))
|
yamlResources, err := ExecuteKustomizeBuild(path.Join(configPath, "manifests", "base", "resources", "users"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected kustomize error: %v\n", err)
|
t.Fatalf("unexpected kustomize error: %v\n", err)
|
||||||
}
|
}
|
||||||
@@ -139,3 +139,97 @@ func getEjsonKeyDir(defaultKeyDir string) string {
|
|||||||
}
|
}
|
||||||
return ejsonKeyDir
|
return ejsonKeyDir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_GetYamlDocKindFromMultiDoc(t *testing.T) {
|
||||||
|
tmpDir, err := ioutil.TempDir("", "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v\n", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
|
kustomizationYamlFilePath := path.Join(tmpDir, "kustomization.yaml")
|
||||||
|
testResFileYamlFilePath := path.Join(tmpDir, "test-file.yaml")
|
||||||
|
kustomizationYaml := `
|
||||||
|
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||||
|
kind: Kustomization
|
||||||
|
resources:
|
||||||
|
- test-file.yaml
|
||||||
|
`
|
||||||
|
testYaml := `
|
||||||
|
apiVersion: v1
|
||||||
|
data:
|
||||||
|
foo: bar
|
||||||
|
kind: ConfigMap
|
||||||
|
metadata:
|
||||||
|
name: foo-config
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: qix-sessions
|
||||||
|
chart: qix-sessions-4.0.10
|
||||||
|
heritage: Helm
|
||||||
|
release: qliksense
|
||||||
|
name: qliksense-qix-sessions
|
||||||
|
namespace: default
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: chronos
|
||||||
|
chart: chronos-1.5.7
|
||||||
|
heritage: Helm
|
||||||
|
release: qliksense
|
||||||
|
name: qliksense-chronos
|
||||||
|
namespace: default
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- endpoints
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- update
|
||||||
|
`
|
||||||
|
err = ioutil.WriteFile(kustomizationYamlFilePath, []byte(kustomizationYaml), os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error writing kustomization file to path: %v error: %v\n", kustomizationYamlFilePath, err)
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(testResFileYamlFilePath, []byte(testYaml), os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error writing test-file to path: %v error: %v\n", testResFileYamlFilePath, err)
|
||||||
|
}
|
||||||
|
result, err := ExecuteKustomizeBuild(tmpDir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected kustomize error: %v\n", err)
|
||||||
|
}
|
||||||
|
resultYaml := GetYamlsFromMultiDoc(string(result), "Role")
|
||||||
|
|
||||||
|
expectedK8sYaml := `
|
||||||
|
---
|
||||||
|
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
|
kind: Role
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: chronos
|
||||||
|
chart: chronos-1.5.7
|
||||||
|
heritage: Helm
|
||||||
|
release: qliksense
|
||||||
|
name: qliksense-chronos
|
||||||
|
namespace: default
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- endpoints
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- update
|
||||||
|
`
|
||||||
|
if resultYaml != expectedK8sYaml {
|
||||||
|
t.Fatalf("expected k8s yaml: [%v] but got: [%v]\n", expectedK8sYaml, resultYaml)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"strings"
|
||||||
|
|
||||||
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
qapi "github.com/qlik-oss/sense-installer/pkg/api"
|
||||||
)
|
)
|
||||||
@@ -41,16 +41,17 @@ func (q *Qliksense) loadCrStringIntoFileSystem(crstr string, overwriteExistingCo
|
|||||||
if !overwriteExistingContext {
|
if !overwriteExistingContext {
|
||||||
return "", errors.New("Context with name: " + cr.GetName() + " already exists. " +
|
return "", errors.New("Context with name: " + cr.GetName() + " already exists. " +
|
||||||
"Please delete the existing context first using the delete-context command or specify the --overwrite flag.")
|
"Please delete the existing context first using the delete-context command or specify the --overwrite flag.")
|
||||||
} else if err := os.RemoveAll(qConfig.GetContextPath(cr.GetName())); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
}
|
||||||
|
// else if err := os.RemoveAll(qConfig.GetContextPath(cr.GetName())); err != nil {
|
||||||
|
// return "", err
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
if err := qConfig.CreateContextDirs(cr.GetName()); err != nil {
|
if err := qConfig.CreateContextDirs(cr.GetName()); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// encrypt the secrets and do base64 then update the CR
|
// encrypt the secrets and do base64 then update the CR
|
||||||
rsaPublicKey, _, err := qConfig.GetContextEncryptionKeyPair(cr.GetName())
|
encryptionKey, err := qConfig.GetEncryptionKeyFor(cr.GetName())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -62,15 +63,26 @@ func (q *Qliksense) loadCrStringIntoFileSystem(crstr string, overwriteExistingCo
|
|||||||
Value: nv.Value,
|
Value: nv.Value,
|
||||||
SvcName: svc,
|
SvcName: svc,
|
||||||
}
|
}
|
||||||
if err := q.processSecret(skv, rsaPublicKey, cr, false); err != nil {
|
if err := q.processSecret(skv, encryptionKey, cr, false); err != nil {
|
||||||
return cr.GetName(), err
|
return cr.GetName(), err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if cr.Spec.FetchSource != nil && cr.Spec.FetchSource.AccessToken != "" {
|
||||||
|
if err := cr.SetFetchAccessToken(cr.Spec.FetchSource.AccessToken, encryptionKey); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// update manifestsRoot in case already exist
|
||||||
|
if existingCr, err := qConfig.GetCR(cr.GetName()); err == nil {
|
||||||
|
// cr exists, so update the manifestsRoot if version exist
|
||||||
|
newV := cr.GetLabelFromCr("version")
|
||||||
|
if strings.HasSuffix(existingCr.Spec.ManifestsRoot, newV) {
|
||||||
|
cr.Spec.ManifestsRoot = existingCr.Spec.ManifestsRoot
|
||||||
|
}
|
||||||
|
}
|
||||||
// write to disk
|
// write to disk
|
||||||
|
|
||||||
if err = qConfig.CreateOrWriteCrAndContext(cr); err != nil {
|
if err = qConfig.CreateOrWriteCrAndContext(cr); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,21 +28,20 @@ func (q *Qliksense) UpgradeQK8s(keepPatchFiles bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
qcr.Spec.RotateKeys = "no"
|
qcr.Spec.RotateKeys = "no"
|
||||||
if dcr, err := qConfig.GetDecryptedCr(qcr); err != nil {
|
|
||||||
return err
|
|
||||||
} else if err := q.applyConfigToK8s(dcr); err != nil {
|
|
||||||
fmt.Println("cannot do kubectl apply on manifests")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Install operator CR into cluster")
|
dcr, err := qConfig.GetDecryptedCr(qcr)
|
||||||
r, err := qcr.GetString()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := qapi.KubectlApply(r, ""); err != nil {
|
if dcr.Spec.Git != nil && dcr.Spec.Git.Repository != "" {
|
||||||
fmt.Println("cannot do kubectl apply on operator CR")
|
// fetching and applying manifest will be in the operator controller
|
||||||
|
// get decrypted cr
|
||||||
|
return q.applyCR(dcr)
|
||||||
}
|
}
|
||||||
return nil
|
err = q.applyConfigToK8s(dcr)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("cannot do kubectl apply on manifests")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return q.applyCR(dcr)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user