Skip to content

Update "no projects" warning in oc status #12328

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions pkg/authorization/authorizer/messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ func NewForbiddenMessageResolver(projectRequestForbiddenTemplate string) *Forbid
}
messageResolver.addRootScopedForbiddenMessageMaker("create", "projectrequests", newTemplateForbiddenMessageMaker(projectRequestDeny))

// projects "get" request rejection
messageResolver.addNamespacedForbiddenMessageMaker("get", "projects", newTemplateForbiddenMessageMaker(`User "{{.User.GetName}}" cannot get project "{{.Namespace}}"`))

return messageResolver
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/cli/cmd/login/loginoptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ func (o *LoginOptions) gatherProjectInfo() error {
projectsList, err := oClient.Projects().List(kapi.ListOptions{})
// if we're running on kube (or likely kube), just set it to "default"
if kerrors.IsNotFound(err) || kerrors.IsForbidden(err) {
fmt.Fprintf(o.Out, "Using \"default\". You can switch projects with '%s project <projectname>':\n\n", o.CommandName)
fmt.Fprintf(o.Out, "Using \"default\". You can switch projects with:\n\n '%s project <projectname>'", o.CommandName)
o.Project = "default"
return nil
}
Expand Down
20 changes: 18 additions & 2 deletions pkg/cmd/cli/cmd/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
kapi "k8s.io/kubernetes/pkg/api"
kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"

loginutil "github.com/openshift/origin/pkg/cmd/cli/cmd/login/util"
"github.com/openshift/origin/pkg/cmd/cli/describe"
"github.com/openshift/origin/pkg/cmd/templates"
"github.com/openshift/origin/pkg/cmd/util/clientcmd"
Expand Down Expand Up @@ -108,6 +109,11 @@ func (o *StatusOptions) Complete(f *clientcmd.Factory, cmd *cobra.Command, baseC
return err
}

rawConfig, err := f.OpenShiftClientConfig().RawConfig()
if err != nil {
return err
}

if o.allNamespaces {
o.namespace = kapi.NamespaceAll
} else {
Expand All @@ -122,15 +128,25 @@ func (o *StatusOptions) Complete(f *clientcmd.Factory, cmd *cobra.Command, baseC
baseCLIName = "oc"
}

currentNamespace := ""
if currentContext, exists := rawConfig.Contexts[rawConfig.CurrentContext]; exists {
currentNamespace = currentContext.Namespace
}

nsFlag := kcmdutil.GetFlagString(cmd, "namespace")
canRequestProjects, _ := loginutil.CanRequestProjects(config, o.namespace)

o.describer = &describe.ProjectStatusDescriber{
K: kclientset,
C: client,
Server: config.Host,
Suggest: o.verbose,

CommandBaseName: baseCLIName,
CommandBaseName: baseCLIName,
RequestedNamespace: nsFlag,
CurrentNamespace: currentNamespace,

Config: config,
CanRequestProjects: canRequestProjects,

// TODO: Remove these and reference them inside the markers using constants.
LogsCommandName: o.logsCommandName,
Expand Down
19 changes: 9 additions & 10 deletions pkg/cmd/cli/describe/projectstatus.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (
kappsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/apps/internalversion"
kautoscalingclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/autoscaling/internalversion"
kcoreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
"k8s.io/kubernetes/pkg/client/restclient"
utilerrors "k8s.io/kubernetes/pkg/util/errors"
"k8s.io/kubernetes/pkg/util/sets"

Expand All @@ -32,7 +31,6 @@ import (
buildgraph "github.com/openshift/origin/pkg/build/graph/nodes"
"github.com/openshift/origin/pkg/client"
loginerrors "github.com/openshift/origin/pkg/cmd/cli/cmd/errors"
loginutil "github.com/openshift/origin/pkg/cmd/cli/cmd/login/util"
deployapi "github.com/openshift/origin/pkg/deploy/api"
deployedges "github.com/openshift/origin/pkg/deploy/graph"
deployanalysis "github.com/openshift/origin/pkg/deploy/graph/analysis"
Expand Down Expand Up @@ -61,9 +59,11 @@ type ProjectStatusDescriber struct {
Suggest bool

// root command used when calling this command
CommandBaseName string
CommandBaseName string
RequestedNamespace string
CurrentNamespace string

Config *restclient.Config
CanRequestProjects bool

LogsCommandName string
SecurityPolicyCommandFormat string
Expand Down Expand Up @@ -158,12 +158,11 @@ func (d *ProjectStatusDescriber) Describe(namespace, name string) (string, error
if !allNamespaces {
p, err := d.C.Projects().Get(namespace)
if err != nil {
// a forbidden error here means that the user has not created
// any projects, and is therefore using a default namespace
// that they cannot list projects from.
if kapierrors.IsForbidden(err) {
canRequestProjects, _ := loginutil.CanRequestProjects(d.Config, namespace)
return loginerrors.NoProjectsExistMessage(canRequestProjects, d.CommandBaseName), nil
// a forbidden error here (without a --namespace value) means that
// the user has not created any projects, and is therefore using a
// default namespace that they cannot list projects from.
if kapierrors.IsForbidden(err) && len(d.RequestedNamespace) == 0 && len(d.CurrentNamespace) == 0 {
return loginerrors.NoProjectsExistMessage(d.CanRequestProjects, d.CommandBaseName), nil
}
if !kapierrors.IsNotFound(err) {
return "", err
Expand Down
6 changes: 0 additions & 6 deletions test/cmd/login.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,9 @@ fi
# remove self-provisioner role from user and test login prompt before creating any projects
os::cmd::expect_success "oadm policy remove-cluster-role-from-group self-provisioner system:authenticated:oauth --config='${login_kubeconfig}'"
os::cmd::expect_success_and_text "oc login --server=${KUBERNETES_MASTER} --certificate-authority='${MASTER_CONFIG_DIR}/ca.crt' -u test-user -p anything" "You don't have any projects. Contact your system administrator to request a project"
# make sure `oc status` re-uses the correct "no projects" message from `oc login` with no self-provisioner role
os::cmd::expect_success_and_text 'oc status' "You don't have any projects. Contact your system administrator to request a project"
os::cmd::expect_success_and_text 'oc status --all-namespaces' "Showing all projects on server"
# make sure standard login prompt is printed once self-provisioner status is restored
os::cmd::expect_success "oadm policy add-cluster-role-to-group self-provisioner system:authenticated:oauth --config='${login_kubeconfig}'"
os::cmd::expect_success_and_text "oc login --server=${KUBERNETES_MASTER} --certificate-authority='${MASTER_CONFIG_DIR}/ca.crt' -u test-user -p anything" "You don't have any projects. You can try to create a new project, by running"
# make sure `oc status` re-uses the correct "no projects" message from `oc login`
os::cmd::expect_success_and_text 'oc status' "You don't have any projects. You can try to create a new project, by running"
os::cmd::expect_success_and_text 'oc status --all-namespaces' "Showing all projects on server"
os::cmd::expect_success 'oc logout'
echo "login and status messages: ok"

Expand Down
82 changes: 82 additions & 0 deletions test/cmd/status.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
#!/bin/bash

set -o errexit
set -o nounset
set -o pipefail

OS_ROOT=$(dirname "${BASH_SOURCE}")/../..
source "${OS_ROOT}/hack/lib/init.sh"
os::log::stacktrace::install
trap os::test::junit::reconcile_output EXIT

# Cleanup cluster resources created by this test
(
set +e
oc delete project project-bar
exit 0
) &>/dev/null

login_kubeconfig="${ARTIFACT_DIR}/login.kubeconfig"
cp "${KUBECONFIG}" "${login_kubeconfig}"

os::test::junit::declare_suite_start "cmd/status"
# login and ensure no current projects exist
os::cmd::expect_success "oc login --server=${KUBERNETES_MASTER} --certificate-authority='${MASTER_CONFIG_DIR}/ca.crt' -u test-user -p anything"
os::cmd::expect_success 'oc delete project --all'
os::cmd::expect_success 'oc logout'

# remove self-provisioner role from user and test login prompt before creating any projects
os::cmd::expect_success "oadm policy remove-cluster-role-from-group self-provisioner system:authenticated:oauth --config='${login_kubeconfig}'"

# login as 'test-user'
os::cmd::expect_success "oc login --server=${KUBERNETES_MASTER} --certificate-authority='${MASTER_CONFIG_DIR}/ca.crt' -u test-user -p anything"

# make sure `oc status` re-uses the correct "no projects" message from `oc login` with no self-provisioner role
os::cmd::expect_success_and_text 'oc status' "You don't have any projects. Contact your system administrator to request a project"
os::cmd::expect_success_and_text 'oc status --all-namespaces' "Showing all projects on server"
# make sure standard login prompt is printed once self-provisioner status is restored
os::cmd::expect_success "oc logout"
os::cmd::expect_success "oadm policy add-cluster-role-to-group self-provisioner system:authenticated:oauth --config='${login_kubeconfig}'"
os::cmd::expect_success_and_text "oc login --server=${KUBERNETES_MASTER} --certificate-authority='${MASTER_CONFIG_DIR}/ca.crt' -u test-user -p anything" "You don't have any projects. You can try to create a new project, by running"

# make sure `oc status` re-uses the correct "no projects" message from `oc login`
os::cmd::expect_success_and_text 'oc status' "You don't have any projects. You can try to create a new project, by running"
os::cmd::expect_success_and_text 'oc status --all-namespaces' "Showing all projects on server"
# make sure `oc status` does not re-use the "no projects" message from `oc login` if -n is specified
os::cmd::expect_failure_and_text 'oc status -n forbidden' 'Error from server \(Forbidden\): User "test-user" cannot get project "forbidden"'

# create a new project
os::cmd::expect_success "oc new-project project-bar --display-name='my project' --description='test project'"
os::cmd::expect_success_and_text "oc project" 'Using project "project-bar"'

# make sure `oc status` does not use "no projects" message if there is a project created
os::cmd::expect_success_and_text 'oc status' "In project my project \(project-bar\) on server"
os::cmd::expect_failure_and_text 'oc status -n forbidden' 'Error from server \(Forbidden\): User "test-user" cannot get project "forbidden"'

# create a second project
os::cmd::expect_success "oc new-project project-bar-2 --display-name='my project 2' --description='test project 2'"
os::cmd::expect_success_and_text "oc project" 'Using project "project-bar-2"'

# delete the current project `project-bar-2` and make sure `oc status` does not return the "no projects"
# message since `project-bar` still exists
os::cmd::expect_success_and_text "oc delete project project-bar-2" 'project "project-bar-2" deleted'
os::cmd::expect_failure_and_text "oc status" 'Error from server \(Forbidden\): User "test-user" cannot get project "project-bar-2"'

# delete "project-bar" and test that `oc status` still does not return the "no projects" message.
# Although we are deleting the last remaining project, the current context's namespace is still set
# to it, therefore `oc status` should simply return a forbidden error and not the "no projects" message
# until the next time the user logs in.
os::cmd::expect_success "oc project project-bar"
os::cmd::expect_success "oc delete project project-bar"
os::cmd::expect_failure_and_text "oc status" 'Error from server \(Forbidden\): User "test-user" cannot get project "project-bar"'
os::cmd::try_until_not_text "oc get projects" "project-bar"
os::cmd::try_until_not_text "oc get projects" "project-bar-2"
os::cmd::expect_success "oc logout"
os::cmd::expect_success_and_text "oc login --server=${KUBERNETES_MASTER} --certificate-authority='${MASTER_CONFIG_DIR}/ca.crt' -u test-user -p anything" "You don't have any projects. You can try to create a new project, by running"
os::cmd::expect_success_and_text 'oc status' "You don't have any projects. You can try to create a new project, by running"

# logout
os::cmd::expect_success "oc logout"

echo "status: ok"
os::test::junit::declare_suite_end
2 changes: 1 addition & 1 deletion test/integration/authorization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -929,7 +929,7 @@ func TestAuthorizationSubjectAccessReview(t *testing.T) {
localReview: askCanValerieGetProject,
response: authorizationapi.SubjectAccessReviewResponse{
Allowed: false,
Reason: `User "valerie" cannot get projects in project "mallet-project"`,
Reason: `User "valerie" cannot get project "mallet-project"`,
Namespace: "mallet-project",
},
}.run(t)
Expand Down