diff --git a/cli/run.go b/cli/sfconfig/cmd/dev/run.go similarity index 55% rename from cli/run.go rename to cli/sfconfig/cmd/dev/run.go index b43c7ea6..a22ce8f7 100644 --- a/cli/run.go +++ b/cli/sfconfig/cmd/dev/run.go @@ -15,44 +15,54 @@ import ( "gopkg.in/yaml.v3" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log/zap" - sfv1 "github.com/softwarefactory-project/sf-operator/api/v1" "github.com/softwarefactory-project/sf-operator/cli/sfconfig/cmd/gerrit" "github.com/softwarefactory-project/sf-operator/cli/sfconfig/cmd/nodepool" "github.com/softwarefactory-project/sf-operator/cli/sfconfig/cmd/sfprometheus" "github.com/softwarefactory-project/sf-operator/cli/sfconfig/cmd/utils" "github.com/softwarefactory-project/sf-operator/cli/sfconfig/config" - controllers "github.com/softwarefactory-project/sf-operator/controllers" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/resource" + "github.com/spf13/cobra" ) -func Run(erase bool) { +var DevCmd = &cobra.Command{ + Use: "dev", + Short: "developer utilities", + Run: func(cmd *cobra.Command, args []string) {}, +} + +var DevPrepareCmd = &cobra.Command{ + Use: "prepare", + Short: "prepare dev environment", + Run: func(cmd *cobra.Command, args []string) { Run() }, +} + +func init() { + DevCmd.AddCommand(DevPrepareCmd) +} + +func Run() { ctrl.SetLogger(zap.New(zap.UseFlagOptions(&zap.Options{Development: true}))) sfconfig := config.GetSFConfigOrDie() fmt.Println("sfconfig started with: ", sfconfig) cli, err := utils.CreateKubernetesClient("") if err != nil { - cli = EnsureCluster(err) + panic(err) } env := utils.ENV{ Ctx: context.TODO(), Ns: "sf", Cli: cli, } - if erase { - fmt.Println("Erasing...") - // TODO: remove the sfconfig resource and the pv - } else { - // TODO: only do gerrit when provision demo is on? - gerrit.EnsureGerrit(&env, sfconfig.FQDN) - sfprometheus.EnsurePrometheus(&env, sfconfig.FQDN, false) - EnsureDemoConfig(&env, &sfconfig) - nodepool.CreateNamespaceForNodepool(&env, "", "nodepool", "") - EnsureDeployment(&env, &sfconfig) - } + // TODO: only do gerrit when provision demo is on? + EnsureNamespacePermissions(&env) + EnsureCertManager(&env) + EnsurePrometheusOperator(&env) + gerrit.EnsureGerrit(&env, sfconfig.FQDN) + sfprometheus.EnsurePrometheus(&env, sfconfig.FQDN, false) + EnsureDemoConfig(&env, &sfconfig) + nodepool.CreateNamespaceForNodepool(&env, "", "nodepool", "") + EnsureCRD() } // EnsureDemoConfig prepares a demo config @@ -121,109 +131,14 @@ func EnsureRepo(sfconfig *config.SFConfig, apiKey string, name string) { utils.RunCmd("git", "-C", path, "reset", "--hard", "origin/master") } -// EnsureCluster recorvers from client creation error -func EnsureCluster(err error) client.Client { - // TODO: perform openstack server reboot? - panic(fmt.Errorf("cluster error: %s", err)) -} - -// EnsureDeployment ensures a deployment is running. -func EnsureDeployment(env *utils.ENV, sfconfig *config.SFConfig) { - fmt.Println("[+] Checking SF resource...") - sf, err := utils.GetSF(env, "my-sf") - if sf.Status.Ready { - // running the operator should be a no-op - RunOperator() - - fmt.Println("Software Factory is already ready!") - // TODO: connect to the Zuul API and ensure it is running - fmt.Println("Check https://zuul." + sf.Spec.FQDN) - os.Exit(0) - - } else if err != nil { - if errors.IsNotFound(err) { - // The resource does not exist - EnsureNamespacePermissions(env) - EnsureCR(env, sfconfig) - EnsureCertManager(env) - EnsurePrometheusOperator(env) - RunOperator() - - } else if utils.IsCRDMissing(err) { - // The resource definition does not exist - EnsureNamespacePermissions(env) - EnsureCRD() - EnsureCR(env, sfconfig) - EnsureCertManager(env) - EnsurePrometheusOperator(env) - RunOperator() - - } else { - // TODO: check what is the actual error and suggest counter measure, for example: - // if microshift host is up but service is done, apply the ansible-microshift-role - // if kubectl is not connecting ask for reboot or rebuild - fmt.Printf("Error %v\n", errors.IsInvalid(err)) - fmt.Println(err) - } - - } else { - // Software Factory resource exists, but it is not ready - if IsOperatorRunning() { - // TODO: check operator status - // TODO: check cluster status and/or suggest sf resource delete/recreate - } else { - EnsureCertManager(env) - EnsurePrometheusOperator(env) - RunOperator() - } - } - - // TODO: suggest sfconfig --erase if the command does not succeed. - fmt.Println("[+] Couldn't deploy your software factory, sorry!") -} - func EnsureNamespacePermissions(env *utils.ENV) { // TODO: implement natively + // TODO: ensure setup-namespaces role use this to avoid duplication utils.RunCmd("kubectl", "label", "--overwrite", "ns", env.Ns, "pod-security.kubernetes.io/enforce=privileged") utils.RunCmd("kubectl", "label", "--overwrite", "ns", env.Ns, "pod-security.kubernetes.io/enforce-version=v1.24") utils.RunCmd("oc", "adm", "policy", "add-scc-to-user", "privileged", "-z", "default") } -func EnsureCR(env *utils.ENV, sfconfig *config.SFConfig) { - fmt.Println("[+] Installing CR...") - var cr sfv1.SoftwareFactory - cr.SetName("my-sf") - cr.SetNamespace(env.Ns) - cr.Spec.FQDN = sfconfig.FQDN - cr.Spec.ConfigLocation = sfv1.ConfigLocationSpec{ - BaseURL: "http://gerrit-httpd/", - Name: "config", - ZuulConnectionName: "gerrit", - } - cr.Spec.Zuul.GerritConns = []sfv1.GerritConnection{ - { - Name: "gerrit", - Username: "zuul", - Hostname: "gerrit-sshd", - Puburl: "https://gerrit." + sfconfig.FQDN, - }, - } - - cr.Spec.StorageClassName = "topolvm-provisioner" - logserverVolumeSize, _ := resource.ParseQuantity("2Gi") - cr.Spec.Logserver.Storage.Size = logserverVolumeSize - var err error - for i := 0; i < 10; i++ { - err = env.Cli.Create(env.Ctx, &cr) - if err == nil { - return - } - // Sometime the api needs a bit of time to register the CRD - time.Sleep(2 * time.Second) - } - panic(fmt.Errorf("could not install CR: %s", err)) -} - func EnsureCRD() { // TODO: implement natively and avoir re-entry fmt.Println("[+] Installing CRD...") @@ -253,13 +168,3 @@ func EnsurePrometheusOperator(env *utils.ENV) { panic(fmt.Errorf("could not install prometheus-operator: %s", err)) } } - -func RunOperator() { - fmt.Println("[+] Running the operator...") - controllers.Main("sf", ":8081", ":8080", false, true) -} - -func IsOperatorRunning() bool { - // TODO: implement - return false -} diff --git a/cli/sfconfig/cmd/root.go b/cli/sfconfig/cmd/root.go index 555f7f95..5b39dea6 100644 --- a/cli/sfconfig/cmd/root.go +++ b/cli/sfconfig/cmd/root.go @@ -21,8 +21,8 @@ import ( "fmt" "os" - "github.com/softwarefactory-project/sf-operator/cli" bootstrap "github.com/softwarefactory-project/sf-operator/cli/sfconfig/cmd/bootstrap-tenant-config-repo" + cli "github.com/softwarefactory-project/sf-operator/cli/sfconfig/cmd/dev" "github.com/softwarefactory-project/sf-operator/cli/sfconfig/cmd/gerrit" "github.com/softwarefactory-project/sf-operator/cli/sfconfig/cmd/nodepool" "github.com/softwarefactory-project/sf-operator/cli/sfconfig/cmd/operator" @@ -40,13 +40,8 @@ var cfgFile string // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ Use: "sfconfig", - Short: "sfconfig cli tool", - Long: `sfconfig command line tool - This tool is used to deploy and run tests for sf-operator`, - Run: func(cmd *cobra.Command, args []string) { - erase, _ := cmd.Flags().GetBool("erase") - cli.Run(erase) - }, + Short: "Toolbox for sf-operator", + Run: func(cmd *cobra.Command, args []string) {}, } // Execute adds all child commands to the root command and sets flags appropriately. @@ -72,9 +67,9 @@ func init() { rootCmd.AddCommand(sfprometheus.PrometheusCmd) rootCmd.AddCommand(nodepool.ProvidersSecretsCmd) rootCmd.AddCommand(zuul.ZuulCmd) - rootCmd.AddCommand(bootstrap.BootstrapTenantConfigRepoCmd) - rootCmd.Flags().BoolP("erase", "", false, "Erase data") + rootCmd.AddCommand(cli.DevCmd) + } // initConfig reads in config file and ENV variables if set. diff --git a/cli/sfconfig/cmd/runTests.go b/cli/sfconfig/cmd/runTests.go index bf5289d3..62691699 100644 --- a/cli/sfconfig/cmd/runTests.go +++ b/cli/sfconfig/cmd/runTests.go @@ -21,16 +21,9 @@ var extravars []string // runTestsCmd represents the runTests command var runTestsCmd = &cobra.Command{ Use: "runTests", - Short: "Run playbook/main.yaml", - Long: `Run playbook/main.yaml playbook, it used for CI job -and can be used locally - -Run test_only tag - -./tools/sfconfig runTests --test-only - `, + Short: "Run the test suite", Run: func(cmd *cobra.Command, args []string) { - testOnly, _ := cmd.Flags().GetBool("test-only") + testOnly, _ := cmd.Flags().GetBool("standalone") upgrade, _ := cmd.Flags().GetBool("upgrade") verbose, _ := cmd.Flags().GetBool("v") debug, _ := cmd.Flags().GetBool("vvv") @@ -59,7 +52,7 @@ Run test_only tag } else { playbookYAML = "playbooks/main.yaml" if testOnly { - ansiblePlaybookOptions.Tags = "test_only" + ansiblePlaybookOptions.Tags = "standalone" ansiblePlaybookOptions.AddExtraVar("mode", "standalone") } else { ansiblePlaybookOptions.AddExtraVar("mode", "olm") @@ -101,7 +94,7 @@ func varListToMap(varsList []string) (map[string]interface{}, error) { func init() { rootCmd.AddCommand(runTestsCmd) runTestsCmd.Flags().StringSliceVarP(&extravars, "extra-var", "e", []string{}, "Set extra variables, the format of each variable must be =") - runTestsCmd.Flags().BoolP("test-only", "t", false, "run test_only") + runTestsCmd.Flags().BoolP("standalone", "s", false, "run standalone test") runTestsCmd.Flags().BoolP("upgrade", "u", false, "run upgrade test") runTestsCmd.Flags().Bool("v", false, "run ansible in verbose mode") runTestsCmd.Flags().Bool("vvv", false, "run ansible in debug mode") diff --git a/doc/developer/getting_started.md b/doc/developer/getting_started.md index d1840c30..c840baa7 100644 --- a/doc/developer/getting_started.md +++ b/doc/developer/getting_started.md @@ -1,14 +1,14 @@ # Getting Started -This section covers the basic tools and the testing environment required to start working on SF-Operator's code base. +This section covers the basics to get started with the development on the SF-Operator's code base. ## Table of Contents 1. [Requirements](#requirements) -1. [Deploy test resources](#deploy-test-resources) -1. [Run the operator in dev mode](#run-the-operator-in-dev-mode) -1. [Next steps](#next-steps) -1. [Experiment a deployment with the standalone mode](#experiment-a-deployment-with-the-standalone-mode) +1. [Run the operator](#run-the-operator) +1. [Access the services web UI](#access-the-services-web-ui) +1. [Delete the development deployment](#delete-the-development-deployment) +1. [To go further](#to-go-further) ## Requirements @@ -31,13 +31,6 @@ The following tools are not mandatory, but they will make your life much easier: - skopeo - buildah -The [operator-sdk](https://sdk.operatorframework.io/) is required to generate/update the OLM bundle, or -when a new `CRD` needs to be added to the operator. You can install it with `make`: - -```sh -make operator-sdk -``` - ### OpenShift You need an OpenShift cluster or equivalent on which to run the operator and deploy resources. @@ -45,20 +38,9 @@ The requirements for the cluster are [the ones listed in the operator section of You can read about [how to deploy a MicroShift instance here](./microshift.md). -## Deploy test resources - -With `sfconfig`, you can quickly deploy a demo deployment consisting of the following: +### Prepare development context -* a SoftwareFactory resource (Zuul, Nodepool, Log server and backend services) -* a companion Gerrit service hosting: - * the deployment's config repository - * a demo repository -* a companion Prometheus instance for monitoring - -The operator will automatically use the current context in your kubeconfig file -(i.e. whatever cluster `kubectl cluster-info` shows). -Make sure that your current context is the right one for development. In this example, we are using -the microshift context: +`sf-operator` uses the current context in your kubeconfig file (i.e. whatever cluster `kubectl cluster-info` shows). Make sure that your current context is the right one for development. In this example, we are using the `microshift` context: ```sh kubectl config current-context @@ -72,74 +54,88 @@ Edit the [sfconfig.yaml](./../../sfconfig.yaml) configuration file to your likin Then run the `sfconfig` command: ```sh -go run ./cli/sfconfig +./tools/sfconfig dev prepare ``` -You can monitor the deployment's advancement by running +This command performs the following tasks: -```sh -kubectl get pods -w -n sf -``` +- ensure namespace permissions +- ensure the installation of the Cert-manager and Prometheus operators +- ensure the deployment of Gerrit +- ensure the deployment of Prometheus +- ensure the checkout of the config and demo-project git repositories in the `deploy` directory +- ensure the creation of dedicated namespace for nodepool-launcher + +The context is now ready to run the sf-operator using the `manager` or the `standalone` modes. + +## Run the operator -The deployment is ready when every pod is either in status "Completed" or "Running". +To iterate on the development of the `sf-operator` you can either start the operator using: -## Run the operator in dev mode +- the `manager` mode: the is the default running mode of the operator. + The `SoftwareFactory's` 's`CRD` must be installed into the cluster, and the operator watches + for a `CR` to reconcile the state in the namespace. +- the `standalone` mode: does not require the installation of the `CRD`. The `controller-runtime`'s + client is used to perform a `SofwareFactory` deployment based on `yaml` definition passed + as parameter. -To run the operator locally, simply do +### Run the operator with the manager mode + +Run the operator with the following command: ```sh go run ./main.go --namespace sf ``` +> The command does not return and wait for events to run the reconcile. + You can kill and restart this process every time you modify the code base to see your changes applied to the deployed resources. -## Next Steps - -You can verify that the services are properly exposed with Firefox (you may have to accept insecure connections when deploying with the default self-signed CA): +In another terminal, apply the `SoftwareFactory`'s `CR`: ```sh -firefox https://zuul. -firefox https://gerrit. -firefox https://logserver. -firefox https://prometheus. -firefox https://nodepool. +kubectl apply -f config/sample/sf_v1_softwarefactory.yaml ``` -Next, you may want to [run the test suite on your modifications](./testing.md). +Any change on the applied resource re-trigger the reconcile. -## Experiment a deployment with the Standalone mode +### Run the operator in standalone mode -The purpose of this mode is to experiment a Software Factory deployment without the need -to get the `sf-operators' CRDs` installed on the cluster. CRDs installation requires the `cluster-admin` -right or the `sf-operator` being installed by your cluster's admin or via `OLM`. - -For instance, you might want to first experiment with a sandbox deployment of Software Factory before requesting -an installation of the sf-operator to your cluster's administrator. - -To experiment with a deployment (assuming a valid `kube config` file and the right `context` set), run the following command: - -> `sf` namespace must have been created prior to the command below. +Run the operator with the following command: ```sh go run ./main.go standalone --cr config/samples/sf_v1_softwarefactory.yaml --namespace sf ``` +> The command returns when the expected state is applied. + Each change to the `CR`, passed as parameter, will require a new run of the command to `reconcile` the change. -To go further, with that deployment, please refer to [set up a **config** repository](../deployment/config_repository.md). -### Delete the sandbox deployment +## Access the services web UI -This deployment mode creates an owner `ConfigMap` Resource that can deleted to trigger the deletion -of all SoftwareFactory's Resources created by the `standalone` command. +You can verify that the services are properly exposed with Firefox (you may have to accept insecure connections when deploying with the default self-signed CA): -``` -kubectl -n sf delete cm sf-standalone-owner +```sh +firefox https://zuul. +firefox https://gerrit. +firefox https://logserver. +firefox https://prometheus. +firefox https://nodepool. ``` -Then, delete the `PVCs`, with: +## Delete the development deployment +Run the deletion with the following command: + +``` +./tools/sfconfig sf delete -a ``` -./tools/sfconfig sf delete --pvcs -``` \ No newline at end of file + +## To go further + +The run to `sfconfig prepare dev` setups a `Gerrit` instance with a [a **config** repository](../deployment/config_repository.md). This repository can be used to play with the deployment. + + +You may want to [run the test suite on your modifications](./testing.md). diff --git a/doc/developer/testing.md b/doc/developer/testing.md index bd70212c..58efc0f9 100644 --- a/doc/developer/testing.md +++ b/doc/developer/testing.md @@ -1,49 +1,64 @@ # Running the test suites locally -Before running the tests, make sure the operator was started in another terminal: +Tests run in the [project's CI](https://zuul.microshift.softwarefactory-project.io/zuul/t/sf/buildsets) can also be run locally using the `sfconfig runTests` CLI. + +> This command is a wrapper on top of `ansible-playbook` to run the same Ansible playbook +than the CI. This includes steps to deploy the operator if needed. + +The command accepts extra Ansible parameters to be passed to `ansible-playbook` command. + +For instance to override the default `microshift_host` variable: ```sh -go run ./main.go --namespace sf +./tools/sfconfig runTests --extra-var "microshift_host=my-microshift" ``` -Tests run in the [project's CI](https://softwarefactory-project.io/zuul/t/local/buildsets?project=software-factory%2Fsf-operator) can also be run locally using the `sfconfig` CLI: +To get more Ansible output logs, you can use the `verbose (-v)` or `debug (-vvv)` parameter. +For example: ```sh -./tools/sfconfig runTests +/tools/sfconfig runTests -v ``` -This command is a wrapper on top of `ansible-playbook` to run the same Ansible playbook -than the CI. This includes steps to deploy the operator if needed. +## Available test suites + +### The OLM validation test -`runTests` performs a build and installation of the `OLM package` of the `sf-operator` prior to -running the validation test suite. +The `OLM` test (similar to `sf-operator-olm` in the Zuul CI) performs a build and +installation of the `OLM package` of the `sf-operator` prior to running the validation +test suite. -If you want to only run the test suite part (the functional tests only, [assuming a SoftwareFactory instance was already deployed](./getting_started.md)), then you can use the `--test-only` option: +To perform this test, run the following command: ```sh -./tools/sfconfig runTests --test-only +./tools/sfconfig runTests ``` -The command accepts extra Ansible parameters to be passed to `ansible-playbook` command. -For instance to override the default `microshift_host` variable: +### The OLM upgrade validation test -```sh -./tools/sfconfig runTests --extra-var "microshift_host=my-microshift" -``` +The `OLM upgrade` test (similar to `sf-operator-upgrade` in the Zuul CI) performs the installation via `OLM` of the current published version of the operator then +build the current local version and upgrade the currently deployed version. +Finally, runs the validation test suite. -To get more Ansible output logs, you can use the `verbose (-v)` or `debug (-vvv)` parameter. -For example: +To run the upgrade sf-operator test scenario, run the following command: ```sh -/tools/sfconfig runTests -v +/tools/sfconfig runTests --upgrade ``` -To run the upgrade sf-operator test scenario, run the command below: +### The standalone validation test + +The `standalone` tests (similar to `sf-operator-dev-multinode` in the Zuul CI) performs +a standalone deployment and run the validation test suite. + +> This is fastest way to run the test suite when iterating on the development of the `sf-operator`. ```sh -/tools/sfconfig runTests --upgrade +./tools/sfconfig runTests --standalone ``` +## Fetching test artifacts + To fetch the test suite artifacts (service logs, operator logs, etc) locally, run: ```sh diff --git a/doc/reference/cli/index.md b/doc/reference/cli/index.md index 53ac1dc5..0a58c96c 100644 --- a/doc/reference/cli/index.md +++ b/doc/reference/cli/index.md @@ -302,7 +302,7 @@ Flags: |----------|------|-------|----| | -e, --extra-var |string | Set extra variables, the format of each variable must be `key`=`value`|-| | -h, --help | boolean | help for runTests|-| -| -t, --test-only | boolean | run tests only - it is assumed the operator is running and a SoftwareFactory resource is already deployed |-| +| -s, --standalone | boolean | run standalone test|-| | -u, --upgrade | boolean | run upgrade test|-| | --v |boolean | run ansible in verbose mode|-| | --vvv | boolean | run ansible in debug mode|-| diff --git a/playbooks/main.yaml b/playbooks/main.yaml index 04675cf1..77ce5d86 100644 --- a/playbooks/main.yaml +++ b/playbooks/main.yaml @@ -37,6 +37,6 @@ name: run-tests apply: tags: - - test_only + - standalone tags: - always diff --git a/playbooks/upgrade.yaml b/playbooks/upgrade.yaml index 520527e8..edb9dc1c 100644 --- a/playbooks/upgrade.yaml +++ b/playbooks/upgrade.yaml @@ -21,12 +21,4 @@ ci_bundle_img: localhost:5000/sf-operator-bundle:latest - upgrade-operator - start-prometheus - tasks: - - name: Run tests - ansible.builtin.include_role: - name: run-tests - apply: - tags: - - test_only - tags: - - always + - run-tests