Merge pull request #54 from apache/0.9.0
0.9.0
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 1eb7cc8..ab71904 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -22,7 +22,7 @@
tags:
- "v*"
branches-ignore:
- - "*"
+ - "**"
permissions:
contents: write
diff --git a/README.md b/README.md
index 8380751..1c21e60 100644
--- a/README.md
+++ b/README.md
@@ -24,6 +24,8 @@
Check [the installation instructions](https://openserverless.apache.org/docs/installation/install-cli/)
+For `ops config sso`, see [SSO command ownership and 0.9.0 prerequisites](docs/SSO.md).
+
**NOTE** in windows there are some features like the ide that requires you use a devcontainer
with `ops ide devcontainer`
@@ -205,7 +207,7 @@
- `OPS_TMP` is a temporary folder where you can store temp files - defaults to `~/.ops/tmp`
- `OPS_APIHOST` is the host for `ops -login`. It is used in place of the first argument of `ops -login`. If empty, the
command will expect the first argument to be the apihost.
-- `OPS_USER` is set the username for `ops -login`. The default is `nuvolaris`. It can be overriden by passing the
+- `OPS_USER` is set the username for `ops -login`. The default is `openserverless`. It can be overriden by passing the
username as an argument to `ops -login` or by setting the environment variable.
- `OPS_PASSWORD`: set the password for `ops -login`. If not set, `ops -login` will prompt for the password. It is useful
for tests and non-interactive environments.
diff --git a/auth/login.go b/auth/login.go
index 3c61a09..7798879 100644
--- a/auth/login.go
+++ b/auth/login.go
@@ -86,7 +86,7 @@
const usage = `Usage:
ops -login [options] <apihost> [<user>]
-Login to an OpenServerless instance. If no user is specified, the default user "nuvolaris" is used.
+Login to an OpenServerless instance. If no user is specified, the default user "openserverless" is used.
You can set the environment variables OPS_APIHOST and OPS_USER to avoid specifying them on the command line.
You can set OPS_PASSWORD to avoid entering the password interactively.
When SSO is enabled, set OPS_SSO_LOGIN_FLOW=password or pass --sso-flow password to use
@@ -105,8 +105,8 @@
const oidcDeviceStartPath = "/system/api/v1/auth/oidc/device/start"
const oidcDevicePollPath = "/system/api/v1/auth/oidc/device/poll"
const oidcPasswordPath = "/system/api/v1/auth/oidc/password"
-const defaultUser = "nuvolaris"
-const opsSecretServiceName = "nuvolaris"
+const defaultUser = "openserverless"
+const opsSecretServiceName = "openserverless"
func LoginCmd() (*LoginResult, error) {
diff --git a/auth/login_test.go b/auth/login_test.go
index 11bb2cd..0c6815f 100644
--- a/auth/login_test.go
+++ b/auth/login_test.go
@@ -84,7 +84,7 @@
})
t.Run("with only apihost, add received credentials", func(t *testing.T) {
- mockServer := setupMockServer(t, "nuvolaris", "a password", "{\"AUTH\": \"test\"}")
+ mockServer := setupMockServer(t, "openserverless", "a password", "{\"AUTH\": \"test\"}")
defer mockServer.Close()
oldPwdReader := pwdReader
diff --git a/config/sso_tool.go b/config/sso_tool.go
index 9e533ea..83c87d0 100644
--- a/config/sso_tool.go
+++ b/config/sso_tool.go
@@ -29,11 +29,11 @@
)
const (
- defaultSSONamespace = "nuvolaris"
+ defaultSSONamespace = "openserverless"
defaultSSOConfigMap = "openserverless-sso-config"
defaultSSOSecret = "openserverless-sso-secret"
- defaultSSOWorkload = "nuvolaris-system-api"
- defaultSSOContainer = "nuvolaris-system-api"
+ defaultSSOWorkload = "openserverless-system-api"
+ defaultSSOContainer = "openserverless-system-api"
)
type commandRunner func(name string, args []string, stdin []byte) ([]byte, error)
@@ -142,11 +142,11 @@
--groups-claim CLAIM OIDC groups claim. Default: groups
--client-id CLIENT_ID OIDC client id. Defaults to --audience when omitted
--client-secret SECRET OIDC confidential client secret stored only in Kubernetes Secret
- --namespace NS Kubernetes namespace. Default: nuvolaris
+ --namespace NS Kubernetes namespace. Default: openserverless
--configmap NAME Kubernetes ConfigMap name. Default: openserverless-sso-config
--secret NAME Kubernetes Secret name. Default: openserverless-sso-secret
- --statefulset NAME admin-api StatefulSet name. Default: nuvolaris-system-api
- --container NAME admin-api container name. Default: nuvolaris-system-api
+ --statefulset NAME admin-api StatefulSet name. Default: openserverless-system-api
+ --container NAME admin-api container name. Default: openserverless-system-api
--no-rollout Do not restart or wait for admin-api rollout
`)
}
diff --git a/config/sso_tool_test.go b/config/sso_tool_test.go
index 9f10229..4176813 100644
--- a/config/sso_tool_test.go
+++ b/config/sso_tool_test.go
@@ -168,8 +168,8 @@
require.Len(t, commands, 3)
require.Equal(t, []string{"apply", "-f", "-"}, commands[0].args)
- require.Equal(t, []string{"-n", "nuvolaris", "get", "statefulset", "nuvolaris-system-api", "-o", "json"}, commands[1].args)
- require.Equal(t, []string{"-n", "nuvolaris", "patch", "statefulset", "nuvolaris-system-api", "--type=json", "-p", commands[2].args[7]}, commands[2].args)
+ require.Equal(t, []string{"-n", "openserverless", "get", "statefulset", "openserverless-system-api", "-o", "json"}, commands[1].args)
+ require.Equal(t, []string{"-n", "openserverless", "patch", "statefulset", "openserverless-system-api", "--type=json", "-p", commands[2].args[7]}, commands[2].args)
var cmObj map[string]interface{}
require.NoError(t, json.Unmarshal([]byte(commands[0].stdin), &cmObj))
@@ -217,10 +217,10 @@
require.Len(t, commands, 5)
require.Equal(t, []string{"apply", "-f", "-"}, commands[0].args)
- require.Equal(t, []string{"-n", "nuvolaris", "get", "statefulset", "nuvolaris-system-api", "-o", "json"}, commands[1].args)
- require.Equal(t, []string{"-n", "nuvolaris", "patch", "statefulset", "nuvolaris-system-api", "--type=json", "-p", commands[2].args[7]}, commands[2].args)
- require.Equal(t, []string{"-n", "nuvolaris", "rollout", "restart", "statefulset/nuvolaris-system-api"}, commands[3].args)
- require.Equal(t, []string{"-n", "nuvolaris", "rollout", "status", "statefulset/nuvolaris-system-api", "--timeout=180s"}, commands[4].args)
+ require.Equal(t, []string{"-n", "openserverless", "get", "statefulset", "openserverless-system-api", "-o", "json"}, commands[1].args)
+ require.Equal(t, []string{"-n", "openserverless", "patch", "statefulset", "openserverless-system-api", "--type=json", "-p", commands[2].args[7]}, commands[2].args)
+ require.Equal(t, []string{"-n", "openserverless", "rollout", "restart", "statefulset/openserverless-system-api"}, commands[3].args)
+ require.Equal(t, []string{"-n", "openserverless", "rollout", "status", "statefulset/openserverless-system-api", "--timeout=180s"}, commands[4].args)
}
func TestConfigSSOToolKeycloakWithClientSecret(t *testing.T) {
@@ -281,8 +281,8 @@
secretData := secretObj["stringData"].(map[string]interface{})
require.Equal(t, "super-secret", secretData["OIDC_CLIENT_SECRET"])
- require.Equal(t, []string{"-n", "nuvolaris", "get", "statefulset", "nuvolaris-system-api", "-o", "json"}, commands[2].args)
- require.Equal(t, []string{"-n", "nuvolaris", "patch", "statefulset", "nuvolaris-system-api", "--type=json", "-p", commands[3].args[7]}, commands[3].args)
+ require.Equal(t, []string{"-n", "openserverless", "get", "statefulset", "openserverless-system-api", "-o", "json"}, commands[2].args)
+ require.Equal(t, []string{"-n", "openserverless", "patch", "statefulset", "openserverless-system-api", "--type=json", "-p", commands[3].args[7]}, commands[3].args)
require.Contains(t, commands[3].args[7], "custom-sso-secret")
}
@@ -336,10 +336,10 @@
},
}, gotConfig)
require.Len(t, commands, 4)
- require.Equal(t, []string{"-n", "nuvolaris", "get", "statefulset", "nuvolaris-system-api", "-o", "json"}, commands[0].args)
- require.Equal(t, []string{"-n", "nuvolaris", "patch", "statefulset", "nuvolaris-system-api", "--type=json", "-p", commands[1].args[7]}, commands[1].args)
- require.Equal(t, []string{"-n", "nuvolaris", "delete", "configmap", "openserverless-sso-config", "--ignore-not-found"}, commands[2].args)
- require.Equal(t, []string{"-n", "nuvolaris", "delete", "secret", "openserverless-sso-secret", "--ignore-not-found"}, commands[3].args)
+ require.Equal(t, []string{"-n", "openserverless", "get", "statefulset", "openserverless-system-api", "-o", "json"}, commands[0].args)
+ require.Equal(t, []string{"-n", "openserverless", "patch", "statefulset", "openserverless-system-api", "--type=json", "-p", commands[1].args[7]}, commands[1].args)
+ require.Equal(t, []string{"-n", "openserverless", "delete", "configmap", "openserverless-sso-config", "--ignore-not-found"}, commands[2].args)
+ require.Equal(t, []string{"-n", "openserverless", "delete", "secret", "openserverless-sso-secret", "--ignore-not-found"}, commands[3].args)
}
func TestConfigSSOToolDisableWaitsForAdminAPIRolloutByDefault(t *testing.T) {
@@ -363,11 +363,11 @@
require.NoError(t, err)
require.Len(t, commands, 5)
- require.Equal(t, []string{"-n", "nuvolaris", "get", "statefulset", "nuvolaris-system-api", "-o", "json"}, commands[0].args)
- require.Equal(t, []string{"-n", "nuvolaris", "patch", "statefulset", "nuvolaris-system-api", "--type=json", "-p", commands[1].args[7]}, commands[1].args)
- require.Equal(t, []string{"-n", "nuvolaris", "delete", "configmap", "openserverless-sso-config", "--ignore-not-found"}, commands[2].args)
- require.Equal(t, []string{"-n", "nuvolaris", "delete", "secret", "openserverless-sso-secret", "--ignore-not-found"}, commands[3].args)
- require.Equal(t, []string{"-n", "nuvolaris", "rollout", "status", "statefulset/nuvolaris-system-api", "--timeout=180s"}, commands[4].args)
+ require.Equal(t, []string{"-n", "openserverless", "get", "statefulset", "openserverless-system-api", "-o", "json"}, commands[0].args)
+ require.Equal(t, []string{"-n", "openserverless", "patch", "statefulset", "openserverless-system-api", "--type=json", "-p", commands[1].args[7]}, commands[1].args)
+ require.Equal(t, []string{"-n", "openserverless", "delete", "configmap", "openserverless-sso-config", "--ignore-not-found"}, commands[2].args)
+ require.Equal(t, []string{"-n", "openserverless", "delete", "secret", "openserverless-sso-secret", "--ignore-not-found"}, commands[3].args)
+ require.Equal(t, []string{"-n", "openserverless", "rollout", "status", "statefulset/openserverless-system-api", "--timeout=180s"}, commands[4].args)
}
func TestConfigSSOToolDisableAlreadyAbsentDoesNotPatchOrRollOut(t *testing.T) {
@@ -392,9 +392,9 @@
require.NoError(t, ConfigSSOTool(cm, []string{"disable"}))
require.Len(t, commands, 3)
- require.Equal(t, []string{"-n", "nuvolaris", "get", "statefulset", "nuvolaris-system-api", "-o", "json"}, commands[0].args)
- require.Equal(t, []string{"-n", "nuvolaris", "delete", "configmap", "openserverless-sso-config", "--ignore-not-found"}, commands[1].args)
- require.Equal(t, []string{"-n", "nuvolaris", "delete", "secret", "openserverless-sso-secret", "--ignore-not-found"}, commands[2].args)
+ require.Equal(t, []string{"-n", "openserverless", "get", "statefulset", "openserverless-system-api", "-o", "json"}, commands[0].args)
+ require.Equal(t, []string{"-n", "openserverless", "delete", "configmap", "openserverless-sso-config", "--ignore-not-found"}, commands[1].args)
+ require.Equal(t, []string{"-n", "openserverless", "delete", "secret", "openserverless-sso-secret", "--ignore-not-found"}, commands[2].args)
}
func TestConfigSSOToolPreservesForeignWorkloadFieldsAcrossEnableDisableEnable(t *testing.T) {
diff --git a/docs/SSO.md b/docs/SSO.md
index d6a51e0..7a93a1a 100644
--- a/docs/SSO.md
+++ b/docs/SSO.md
@@ -17,13 +17,108 @@
~ under the License.
-->
-# Legacy embedded SSO configuration ownership
+# SSO command ownership and 0.9.0 prerequisites
-The public `ops config sso` command is supplied by the task repository. The
-embedded `ops -config sso` form remains temporarily available for compatibility
-with already published scripts and installations. It manages the following
-configuration. Resource names can be changed with `--configmap`, `--secret`,
-`--statefulset`, and `--container`.
+The public `ops config sso` command requires SSO tasks from
+`apache/openserverless-task` and an OIDC-capable `apache/openserverless-admin-api`.
+At the branch snapshots below, those components are missing from `0.9.0`, even
+though the CLI already contains the SSO configuration and login code from
+`0.9.1`. Updating the CLI alone does not enable SSO on a `0.9.0` installation.
+
+## Command ownership
+
+| Command | Implementation | Responsibility |
+| --- | --- | --- |
+| `ops config sso ...` | Task repository: `config/sso/docopts.md`, `config/sso/opsfile.yml`, `config/sso/sso.ts` | Public configuration command, executed with Bun and kubectl. |
+| `ops -config sso ...` | CLI: [config/config_tool.go](../config/config_tool.go), [config/sso_tool.go](../config/sso_tool.go) | Legacy embedded configuration tool. |
+| `ops -login ...` | CLI: [auth/login.go](../auth/login.go) | SSO device and password login through admin-api, alongside legacy login. |
+| `ops ide login ...` | Task repository: `ide/opsfile.yml` | Workspace setup and delegation to the embedded login tool. |
+
+The CLI routes `ops config sso` through its normal task lookup. It does not
+fall back to the embedded configuration tool when the SSO task is missing.
+The embedded form remains temporarily available for compatibility with
+already published scripts and installations; it still requires an
+OIDC-capable admin-api for SSO login to work.
+
+## Branch comparison
+
+Checked on 2026-09-10. The links pin the reviewed commits because branch heads
+can change independently across repositories.
+
+| Repository | `0.9.0` snapshot | `0.9.1` snapshot | SSO status in `0.9.0` |
+| --- | --- | --- | --- |
+| `apache/openserverless-cli` | [83075cbc](https://github.com/apache/openserverless-cli/tree/83075cbc890dd9692782e27340fd2eee6b498a59) | [a0dd56cf](https://github.com/apache/openserverless-cli/tree/a0dd56cf5667acb092cfbac14de531f5de139af0) | `config/` and `auth/`, including their tests, are identical to `0.9.1`. No SSO code backport is needed in the CLI. |
+| `apache/openserverless-task` | [eeac57e4](https://github.com/apache/openserverless-task/tree/eeac57e4179381ee18fba290ab79d6cec1dafb09) | [71bd01f6](https://github.com/apache/openserverless-task/tree/71bd01f649d4f6b551240b7a3fa1db47954f4c6b) | Missing `config/sso/`, SSO help discovery, `admin/sso/`, and SSO changes to IDE login and user lifecycle tasks. |
+| `apache/openserverless-admin-api` | [161c479f](https://github.com/apache/openserverless-admin-api/tree/161c479f318f932e58eced08acd0830e7e75b288) | [35f9a1bc](https://github.com/apache/openserverless-admin-api/tree/35f9a1bcac5f75aab6dfac07d7efa6ac1bd0ae56) | Missing the OIDC bridge, device/password endpoints, SSO namespace mapping and login-triggered provisioning. |
+| `apache/openserverless-testing` | [be7b3338](https://github.com/apache/openserverless-testing/tree/be7b33382e02c6543d3e8813724b0177fae1c18d) | [e017ff5f](https://github.com/apache/openserverless-testing/tree/e017ff5fb5b1147cdb772aa584223d6e0d6c83da) | Missing `tests/11-sso-mock.sh` and `tests/mock-oidc-provider.py`. |
+
+The external components to review for a separate backport are:
+
+- **Configuration tasks:** `config/sso/` (including `sso.test.ts`) and the SSO
+ entries in `config/docopts.md`. The task uses Bun and kubectl from the task
+ prerequisites.
+- **Login and lifecycle tasks:** SSO handling in `ide/opsfile.yml`,
+ `ide/docopts.md`, `admin/opsfile.yml`, `admin/docopts.md`, `admin/sso/`, and
+ SSO display columns in `setup/kubernetes/crds/whisk-user-crd.yaml`.
+- **Admin-api:** `openserverless/common/oidc_validator.py`,
+ `openserverless/common/sso_namespace.py`,
+ `openserverless/impl/auth/oidc_device_flow_service.py`, the SSO additions in
+ `openserverless/impl/auth/auth_service.py` and `openserverless/rest/auth.py`,
+ their dependencies, configuration documentation and tests. These supply
+ `/auth/oidc`, `/auth/oidc/device/start`,
+ `/auth/oidc/device/poll`, and `/auth/oidc/password`, all under
+ `/system/api/v1`.
+- **Integration tests:** the mock provider and SSO smoke script in the testing
+ repository. They complement the CLI unit tests with a deployed admin-api
+ and Kubernetes resources.
+
+Setting local `SSO_*` values or creating a ConfigMap cannot add the missing
+server endpoints. A coordinated task/admin-api backport and integration
+validation are required before the public SSO flow can be considered usable
+with `0.9.0`.
+
+With the `0.9.0` task snapshot above, `ops config sso --help` and
+`ops config sso show` exit with `no command named sso found`.
+`ops -config sso --help` still succeeds because that command is embedded in
+the CLI. The same CLI can resolve `ops config sso --help` with the `0.9.1`
+tasks; this verifies command discovery, not server-side SSO compatibility.
+
+## Kubernetes compatibility
+
+The `0.9.0` deployment manifests and the SSO defaults use different names:
+
+| Resource | `0.9.0` deployment | Embedded SSO / `0.9.1` deployment |
+| --- | --- | --- |
+| Namespace | `openserverless` | `openserverless` |
+| Admin-api StatefulSet and container | `openserverless-system-api` | `openserverless-system-api` |
+| `WhiskUser` API version | `openserverless.org/v1` | `openserverless.org/v1` in the `0.9.1` admin-api/operator |
+
+See the task repository's
+[0.9.0 admin-api template](https://github.com/apache/openserverless-task/blob/eeac57e4179381ee18fba290ab79d6cec1dafb09/setup/openserverless/system-api/api-template.yaml)
+and the operator's
+[0.9.0 WhiskUser CRD](https://github.com/apache/openserverless-operator/blob/5df76feda569b374276f4e2c72a46d3a980a4b77/deploy/openserverless-permissions/whisk-user-crd.yaml).
+The corresponding
+[0.9.1 CRD](https://github.com/apache/openserverless-operator/blob/fdcbbdf2332ae5aa8e22afba0704b37fdb1697bd/deploy/openserverless-permissions/whisk-user-crd.yaml)
+uses `openserverless.org`.
+
+`--namespace`, `--statefulset`, and `--container` select the resources patched
+by the configuration command. They do not change the namespace or API group
+used internally by the
+[0.9.1 admin-api provisioning code](https://github.com/apache/openserverless-admin-api/blob/35f9a1bcac5f75aab6dfac07d7efa6ac1bd0ae56/openserverless/impl/auth/auth_service.py)
+and its
+[Kubernetes client](https://github.com/apache/openserverless-admin-api/blob/35f9a1bcac5f75aab6dfac07d7efa6ac1bd0ae56/openserverless/common/kube_api_client.py).
+A future backport must align those values with the deployed operator and CRD;
+copying the `0.9.1` image or tasks without adaptation is insufficient.
+
+The external-IdP flow uses the operator's existing `WhiskUser` reconciliation.
+Configuring SSO does not install Keycloak or add a Keycloak reconciler to the
+operator. An external Keycloak/IdP must already be available.
+
+## Legacy embedded configuration ownership
+
+`ops -config sso` manages the configuration described below. Resource names
+can be changed with `--namespace`, `--configmap`, `--secret`, `--statefulset`,
+and `--container`.
## Kubernetes resources
@@ -56,7 +151,7 @@
## Disable behavior
-`ops config sso disable` removes only the exact `envFrom` references described
+`ops -config sso disable` removes only the exact `envFrom` references described
above and deletes the two dedicated resources with Kubernetes
`--ignore-not-found`. Other `envFrom` entries, all direct `env` entries,
volumes, volume mounts, and existing annotations remain unchanged.
diff --git a/tests/awk.bats b/tests/awk.bats
index ab4752a..e0274f6 100644
--- a/tests/awk.bats
+++ b/tests/awk.bats
@@ -44,8 +44,8 @@
}
@test "-awk replace" {
- run echo "$(echo "Hello World" | ops -awk '{$2="Nuvolaris"; print $0}')"
- assert_line "Hello Nuvolaris"
+ run echo "$(echo "Hello World" | ops -awk '{$2="OpenServerless"; print $0}')"
+ assert_line "Hello OpenServerless"
assert_success
}
diff --git a/tests/login.bats b/tests/login.bats
index f1f8557..3fc8f77 100644
--- a/tests/login.bats
+++ b/tests/login.bats
@@ -34,8 +34,8 @@
@test "ops -login with OPS_PASSWORD env does not prompt for password" {
export OPS_PASSWORD=1234
- run ops -login nuvolaris.dev
- assert_line --partial "Logging in https://nuvolaris.dev"
+ run ops -login openserverless.dev
+ assert_line --partial "Logging in https://openserverless.dev"
refute_line "Enter Password:"
}
@@ -61,7 +61,7 @@
unset OPS_USER
run ops -login
assert_failure
- assert_line "Logging in http://localhost as nuvolaris"
+ assert_line "Logging in http://localhost as openserverless"
}
@test "ops -login with OPS_APIHOST and OPS_USER env" {
diff --git a/tests/test_helper/fail_then_succeed.sh b/tests/test_helper/fail_then_succeed.sh
index f1de160..737d5fb 100755
--- a/tests/test_helper/fail_then_succeed.sh
+++ b/tests/test_helper/fail_then_succeed.sh
@@ -1,4 +1,21 @@
#!/bin/bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+
file_path=".test_fail_then_succeed"