Compare commits
21 Commits
v3.2.0-alp
...
v3.2.0-rc.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9720aa9806 | ||
|
|
0e3159e1e8 | ||
|
|
edcd8e1449 | ||
|
|
98b9009a58 | ||
|
|
e82236366d | ||
|
|
776593001e | ||
|
|
6dc99e181e | ||
|
|
735f0c7731 | ||
|
|
2af76aff79 | ||
|
|
7a0dafd59d | ||
|
|
813c341574 | ||
|
|
59e03a0c19 | ||
|
|
2d066f86d4 | ||
|
|
b94c7966d1 | ||
|
|
6b3af2d19d | ||
|
|
d8fc168948 | ||
|
|
1641b9920b | ||
|
|
1956f83af0 | ||
|
|
22a8bebcae | ||
|
|
d0ae0f210f | ||
|
|
8ef0605858 |
6
.github/workflows/build-multiarch.yaml
vendored
6
.github/workflows/build-multiarch.yaml
vendored
@@ -24,13 +24,13 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: "Set up QEMU"
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
with:
|
||||
platforms: all
|
||||
|
||||
- name: "Set up Docker buildx"
|
||||
uses: "docker/setup-buildx-action@v1"
|
||||
- name: Set up Docker buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Build and push docker images
|
||||
env:
|
||||
|
||||
8
.github/workflows/nightly-builds.yml
vendored
8
.github/workflows/nightly-builds.yml
vendored
@@ -26,6 +26,14 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
with:
|
||||
platforms: all
|
||||
|
||||
- name: Set up Docker buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Build and push docker images
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
|
||||
@@ -171,6 +171,6 @@ The [user case studies](https://kubesphere.io/case/) page includes the user list
|
||||
<img src="https://landscape.cncf.io/images/left-logo.svg" width="150"/> <img src="https://landscape.cncf.io/images/right-logo.svg" width="200"/>
|
||||
<br/><br/>
|
||||
KubeSphere is a member of CNCF and a <a href="https://www.cncf.io/certification/software-conformance/#logos">Kubernetes Conformance Certified platform
|
||||
</a>, which enriches the <a href="https://landscape.cncf.io/landscape=observability-and-analysis&license=apache-license-2-0">CNCF CLOUD NATIVE Landscape.
|
||||
</a>, which enriches the <a href="https://landscape.cncf.io/?landscape=observability-and-analysis&license=apache-license-2-0">CNCF CLOUD NATIVE Landscape.
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -118,6 +118,6 @@ KubeSphere 3.1.0 已于 2021 年 4 月 29 日正式 GA!点击 [Release Notes F
|
||||
<img src="https://landscape.cncf.io/images/left-logo.svg" width="150"/> <img src="https://landscape.cncf.io/images/right-logo.svg" width="200"/> <img src="https://www.cncf.io/wp-content/uploads/2017/11/certified_kubernetes_color.png" height="40" width="30"/>
|
||||
<br/><br/>
|
||||
KubeSphere 是 CNCF 基金会成员并且通过了 <a href="https://www.cncf.io/certification/software-conformance/#logos">Kubernetes 一致性认证
|
||||
</a>,进一步丰富了 <a href="https://landscape.cncf.io/landscape=observability-and-analysis&license=apache-license-2-0">CNCF 云原生的生态。
|
||||
</a>,进一步丰富了 <a href="https://landscape.cncf.io/?landscape=observability-and-analysis&license=apache-license-2-0">CNCF 云原生的生态。
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -80,7 +80,8 @@ kube::version::get_version_vars() {
|
||||
# the "major" and "minor" versions and whether this is the exact tagged
|
||||
# version or whether the tree is between two tagged versions.
|
||||
if [[ "${KUBE_GIT_VERSION}" =~ ^v([0-9]+)\.([0-9]+)(\.[0-9]+)?([-].*)?([+].*)?$ ]]; then
|
||||
# KUBE_GIT_MAJOR=${BASH_REMATCH[1]}
|
||||
# shellcheck disable=SC2034
|
||||
KUBE_GIT_MAJOR=${BASH_REMATCH[1]}
|
||||
KUBE_GIT_MINOR=${BASH_REMATCH[2]}
|
||||
if [[ -n "${BASH_REMATCH[4]}" ]]; then
|
||||
KUBE_GIT_MINOR+="+"
|
||||
|
||||
@@ -24,6 +24,8 @@ import (
|
||||
rt "runtime"
|
||||
"time"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/utils/iputil"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/authentication/token"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/authorization"
|
||||
@@ -107,7 +109,6 @@ import (
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/s3"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/sonarqube"
|
||||
"kubesphere.io/kubesphere/pkg/utils/metrics"
|
||||
utilnet "kubesphere.io/kubesphere/pkg/utils/net"
|
||||
)
|
||||
|
||||
type APIServer struct {
|
||||
@@ -596,7 +597,7 @@ func logRequestAndResponse(req *restful.Request, resp *restful.Response, chain *
|
||||
}
|
||||
|
||||
logWithVerbose.Infof("%s - \"%s %s %s\" %d %d %dms",
|
||||
utilnet.GetRequestIP(req.Request),
|
||||
iputil.RemoteIp(req.Request),
|
||||
req.Request.Method,
|
||||
req.Request.URL,
|
||||
req.Request.Proto,
|
||||
|
||||
@@ -25,6 +25,8 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/utils/iputil"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/validation/path"
|
||||
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metainternalversionscheme "k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme"
|
||||
@@ -36,7 +38,6 @@ import (
|
||||
|
||||
"kubesphere.io/kubesphere/pkg/api"
|
||||
"kubesphere.io/kubesphere/pkg/constants"
|
||||
netutils "kubesphere.io/kubesphere/pkg/utils/net"
|
||||
)
|
||||
|
||||
type RequestInfoResolver interface {
|
||||
@@ -127,7 +128,7 @@ func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, er
|
||||
},
|
||||
Workspace: api.WorkspaceNone,
|
||||
Cluster: api.ClusterNone,
|
||||
SourceIP: netutils.GetRequestIP(req),
|
||||
SourceIP: iputil.RemoteIp(req),
|
||||
UserAgent: req.UserAgent(),
|
||||
}
|
||||
|
||||
|
||||
@@ -112,9 +112,8 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
|
||||
func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) {
|
||||
logger := r.Logger.WithValues("user", req.NamespacedName)
|
||||
rootCtx := context.Background()
|
||||
user := &iamv1alpha2.User{}
|
||||
err := r.Get(rootCtx, req.NamespacedName, user)
|
||||
err := r.Get(ctx, req.NamespacedName, user)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
@@ -124,7 +123,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
|
||||
// then lets add the finalizer and update the object.
|
||||
if !sliceutil.HasString(user.Finalizers, finalizer) {
|
||||
user.ObjectMeta.Finalizers = append(user.ObjectMeta.Finalizers, finalizer)
|
||||
if err = r.Update(context.Background(), user, &client.UpdateOptions{}); err != nil {
|
||||
if err = r.Update(ctx, user, &client.UpdateOptions{}); err != nil {
|
||||
logger.Error(err, "failed to update user")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
@@ -168,7 +167,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
|
||||
return item == finalizer
|
||||
})
|
||||
|
||||
if err = r.Update(context.Background(), user, &client.UpdateOptions{}); err != nil {
|
||||
if err = r.Update(ctx, user, &client.UpdateOptions{}); err != nil {
|
||||
klog.Error(err)
|
||||
r.Recorder.Event(user, corev1.EventTypeWarning, failedSynced, fmt.Sprintf(syncFailMessage, err))
|
||||
return ctrl.Result{}, err
|
||||
@@ -199,12 +198,12 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
|
||||
// update user status if not managed by kubefed
|
||||
managedByKubefed := user.Labels[constants.KubefedManagedLabel] == "true"
|
||||
if !managedByKubefed {
|
||||
if user, err = r.encryptPassword(user); err != nil {
|
||||
if err = r.encryptPassword(ctx, user); err != nil {
|
||||
klog.Error(err)
|
||||
r.Recorder.Event(user, corev1.EventTypeWarning, failedSynced, fmt.Sprintf(syncFailMessage, err))
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
if user, err = r.syncUserStatus(ctx, user); err != nil {
|
||||
if err = r.syncUserStatus(ctx, user); err != nil {
|
||||
klog.Error(err)
|
||||
r.Recorder.Event(user, corev1.EventTypeWarning, failedSynced, fmt.Sprintf(syncFailMessage, err))
|
||||
return ctrl.Result{}, err
|
||||
@@ -239,15 +238,15 @@ func (r *Reconciler) Reconcile(ctx context.Context, req reconcile.Request) (reco
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *Reconciler) encryptPassword(user *iamv1alpha2.User) (*iamv1alpha2.User, error) {
|
||||
// encryptPassword Encrypt and update the user password
|
||||
func (r *Reconciler) encryptPassword(ctx context.Context, user *iamv1alpha2.User) error {
|
||||
// password is not empty and not encrypted
|
||||
if user.Spec.EncryptedPassword != "" && !isEncrypted(user.Spec.EncryptedPassword) {
|
||||
password, err := encrypt(user.Spec.EncryptedPassword)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
user = user.DeepCopy()
|
||||
user.Spec.EncryptedPassword = password
|
||||
if user.Annotations == nil {
|
||||
user.Annotations = make(map[string]string)
|
||||
@@ -255,32 +254,31 @@ func (r *Reconciler) encryptPassword(user *iamv1alpha2.User) (*iamv1alpha2.User,
|
||||
user.Annotations[iamv1alpha2.LastPasswordChangeTimeAnnotation] = time.Now().UTC().Format(time.RFC3339)
|
||||
// ensure plain text password won't be kept anywhere
|
||||
delete(user.Annotations, corev1.LastAppliedConfigAnnotation)
|
||||
err = r.Update(context.Background(), user, &client.UpdateOptions{})
|
||||
err = r.Update(ctx, user, &client.UpdateOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
return user, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Reconciler) ensureNotControlledByKubefed(user *iamv1alpha2.User) error {
|
||||
func (r *Reconciler) ensureNotControlledByKubefed(ctx context.Context, user *iamv1alpha2.User) error {
|
||||
if user.Labels[constants.KubefedManagedLabel] != "false" {
|
||||
if user.Labels == nil {
|
||||
user.Labels = make(map[string]string, 0)
|
||||
}
|
||||
user = user.DeepCopy()
|
||||
user.Labels[constants.KubefedManagedLabel] = "false"
|
||||
err := r.Update(context.Background(), user, &client.UpdateOptions{})
|
||||
err := r.Update(ctx, user, &client.UpdateOptions{})
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Reconciler) multiClusterSync(ctx context.Context, user *iamv1alpha2.User) error {
|
||||
if err := r.ensureNotControlledByKubefed(user); err != nil {
|
||||
if err := r.ensureNotControlledByKubefed(ctx, user); err != nil {
|
||||
klog.Error(err)
|
||||
return err
|
||||
}
|
||||
@@ -434,12 +432,18 @@ func (r *Reconciler) deleteRoleBindings(ctx context.Context, user *iamv1alpha2.U
|
||||
return err
|
||||
}
|
||||
|
||||
roleBinding := &rbacv1.RoleBinding{}
|
||||
err = r.Client.DeleteAllOf(ctx, roleBinding, client.MatchingLabels{iamv1alpha2.UserReferenceLabel: user.Name})
|
||||
roleBindingList := &rbacv1.RoleBindingList{}
|
||||
err = r.Client.List(ctx, roleBindingList, client.MatchingLabels{iamv1alpha2.UserReferenceLabel: user.Name})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, roleBinding := range roleBindingList.Items {
|
||||
err = r.Client.Delete(ctx, &roleBinding)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -448,57 +452,51 @@ func (r *Reconciler) deleteLoginRecords(ctx context.Context, user *iamv1alpha2.U
|
||||
return r.Client.DeleteAllOf(ctx, loginRecord, client.MatchingLabels{iamv1alpha2.UserReferenceLabel: user.Name})
|
||||
}
|
||||
|
||||
// syncUserStatus will reconcile user state based on user login records
|
||||
func (r *Reconciler) syncUserStatus(ctx context.Context, user *iamv1alpha2.User) (*iamv1alpha2.User, error) {
|
||||
// syncUserStatus Update the user status
|
||||
func (r *Reconciler) syncUserStatus(ctx context.Context, user *iamv1alpha2.User) error {
|
||||
if user.Spec.EncryptedPassword == "" {
|
||||
if user.Labels[iamv1alpha2.IdentifyProviderLabel] != "" {
|
||||
// mapped user from other identity provider always active until disabled
|
||||
if user.Status.State == nil || *user.Status.State != iamv1alpha2.UserActive {
|
||||
expected := user.DeepCopy()
|
||||
active := iamv1alpha2.UserActive
|
||||
expected.Status = iamv1alpha2.UserStatus{
|
||||
user.Status = iamv1alpha2.UserStatus{
|
||||
State: &active,
|
||||
LastTransitionTime: &metav1.Time{Time: time.Now()},
|
||||
}
|
||||
err := r.Update(ctx, expected, &client.UpdateOptions{})
|
||||
err := r.Update(ctx, user, &client.UpdateOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
return expected, nil
|
||||
}
|
||||
} else {
|
||||
// becomes disabled after setting a blank password
|
||||
if user.Status.State == nil || *user.Status.State != iamv1alpha2.UserDisabled {
|
||||
expected := user.DeepCopy()
|
||||
disabled := iamv1alpha2.UserDisabled
|
||||
expected.Status = iamv1alpha2.UserStatus{
|
||||
user.Status = iamv1alpha2.UserStatus{
|
||||
State: &disabled,
|
||||
LastTransitionTime: &metav1.Time{Time: time.Now()},
|
||||
}
|
||||
err := r.Update(ctx, expected, &client.UpdateOptions{})
|
||||
err := r.Update(ctx, user, &client.UpdateOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
return expected, nil
|
||||
}
|
||||
}
|
||||
return user, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// becomes active after password encrypted
|
||||
if isEncrypted(user.Spec.EncryptedPassword) {
|
||||
if user.Status.State == nil || *user.Status.State == iamv1alpha2.UserDisabled {
|
||||
expected := user.DeepCopy()
|
||||
active := iamv1alpha2.UserActive
|
||||
expected.Status = iamv1alpha2.UserStatus{
|
||||
user.Status = iamv1alpha2.UserStatus{
|
||||
State: &active,
|
||||
LastTransitionTime: &metav1.Time{Time: time.Now()},
|
||||
}
|
||||
err := r.Update(ctx, expected, &client.UpdateOptions{})
|
||||
err := r.Update(ctx, user, &client.UpdateOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
return expected, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,18 +504,17 @@ func (r *Reconciler) syncUserStatus(ctx context.Context, user *iamv1alpha2.User)
|
||||
if user.Status.State != nil && *user.Status.State == iamv1alpha2.UserAuthLimitExceeded {
|
||||
if user.Status.LastTransitionTime != nil &&
|
||||
user.Status.LastTransitionTime.Add(r.AuthenticationOptions.AuthenticateRateLimiterDuration).Before(time.Now()) {
|
||||
expected := user.DeepCopy()
|
||||
// unblock user
|
||||
active := iamv1alpha2.UserActive
|
||||
expected.Status = iamv1alpha2.UserStatus{
|
||||
user.Status = iamv1alpha2.UserStatus{
|
||||
State: &active,
|
||||
LastTransitionTime: &metav1.Time{Time: time.Now()},
|
||||
}
|
||||
err := r.Update(ctx, expected, &client.UpdateOptions{})
|
||||
err := r.Update(ctx, user, &client.UpdateOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
return expected, nil
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,7 +523,7 @@ func (r *Reconciler) syncUserStatus(ctx context.Context, user *iamv1alpha2.User)
|
||||
err := r.List(ctx, records, client.MatchingLabels{iamv1alpha2.UserReferenceLabel: user.Name})
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
// count failed login attempts during last AuthenticateRateLimiterDuration
|
||||
@@ -541,22 +538,20 @@ func (r *Reconciler) syncUserStatus(ctx context.Context, user *iamv1alpha2.User)
|
||||
|
||||
// block user if failed login attempts exceeds maximum tries setting
|
||||
if failedLoginAttempts >= r.AuthenticationOptions.AuthenticateRateLimiterMaxTries {
|
||||
expected := user.DeepCopy()
|
||||
limitExceed := iamv1alpha2.UserAuthLimitExceeded
|
||||
expected.Status = iamv1alpha2.UserStatus{
|
||||
user.Status = iamv1alpha2.UserStatus{
|
||||
State: &limitExceed,
|
||||
Reason: fmt.Sprintf("Failed login attempts exceed %d in last %s", failedLoginAttempts, r.AuthenticationOptions.AuthenticateRateLimiterDuration),
|
||||
LastTransitionTime: &metav1.Time{Time: time.Now()},
|
||||
}
|
||||
|
||||
err = r.Update(context.Background(), expected, &client.UpdateOptions{})
|
||||
err = r.Update(ctx, user, &client.UpdateOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
return expected, nil
|
||||
}
|
||||
|
||||
return user, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func encrypt(password string) (string, error) {
|
||||
|
||||
@@ -99,7 +99,7 @@ func TestDoNothing(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, err = c.Reconcile(context.Background(), reconcile.Request{
|
||||
result, err := c.Reconcile(context.Background(), reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Name: user.Name},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -108,22 +108,15 @@ func TestDoNothing(t *testing.T) {
|
||||
|
||||
// append finalizer
|
||||
updateEvent := <-w.ResultChan()
|
||||
assert.Equal(t, updateEvent.Type, watch.Modified)
|
||||
assert.Equal(t, watch.Modified, updateEvent.Type)
|
||||
assert.NotNil(t, updateEvent.Object)
|
||||
user = updateEvent.Object.(*iamv1alpha2.User)
|
||||
assert.NotNil(t, user)
|
||||
assert.NotEmpty(t, user.Finalizers)
|
||||
|
||||
result, err := c.Reconcile(context.Background(), reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{Name: user.Name},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
updateEvent = <-w.ResultChan()
|
||||
// encrypt password
|
||||
assert.Equal(t, updateEvent.Type, watch.Modified)
|
||||
assert.Equal(t, watch.Modified, updateEvent.Type)
|
||||
assert.NotNil(t, updateEvent.Object)
|
||||
user = updateEvent.Object.(*iamv1alpha2.User)
|
||||
assert.NotNil(t, user)
|
||||
@@ -132,12 +125,12 @@ func TestDoNothing(t *testing.T) {
|
||||
// becomes active after password encrypted
|
||||
updateEvent = <-w.ResultChan()
|
||||
user = updateEvent.Object.(*iamv1alpha2.User)
|
||||
assert.Equal(t, *user.Status.State, iamv1alpha2.UserActive)
|
||||
assert.Equal(t, iamv1alpha2.UserActive, *user.Status.State)
|
||||
|
||||
// block user
|
||||
updateEvent = <-w.ResultChan()
|
||||
user = updateEvent.Object.(*iamv1alpha2.User)
|
||||
assert.Equal(t, *user.Status.State, iamv1alpha2.UserAuthLimitExceeded)
|
||||
assert.Equal(t, iamv1alpha2.UserAuthLimitExceeded, *user.Status.State)
|
||||
assert.True(t, result.Requeue)
|
||||
|
||||
time.Sleep(result.RequeueAfter + time.Second)
|
||||
@@ -151,5 +144,5 @@ func TestDoNothing(t *testing.T) {
|
||||
// unblock user
|
||||
updateEvent = <-w.ResultChan()
|
||||
user = updateEvent.Object.(*iamv1alpha2.User)
|
||||
assert.Equal(t, *user.Status.State, iamv1alpha2.UserActive)
|
||||
assert.Equal(t, iamv1alpha2.UserActive, *user.Status.State)
|
||||
}
|
||||
|
||||
@@ -122,7 +122,6 @@ func (r *Reconciler) bindWorkspace(ctx context.Context, logger logr.Logger, work
|
||||
return client.IgnoreNotFound(err)
|
||||
}
|
||||
if !metav1.IsControlledBy(workspaceRole, &workspace) {
|
||||
workspaceRole = workspaceRole.DeepCopy()
|
||||
workspaceRole.OwnerReferences = k8sutil.RemoveWorkspaceOwnerReference(workspaceRole.OwnerReferences)
|
||||
if err := controllerutil.SetControllerReference(&workspace, workspaceRole, r.Scheme); err != nil {
|
||||
logger.Error(err, "set controller reference failed")
|
||||
@@ -151,6 +150,7 @@ func (r *Reconciler) multiClusterSync(ctx context.Context, logger logr.Logger, w
|
||||
logger.Error(err, "create federated workspace role failed")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
logger.Error(err, "get federated workspace role failed")
|
||||
@@ -174,10 +174,6 @@ func (r *Reconciler) multiClusterSync(ctx context.Context, logger logr.Logger, w
|
||||
|
||||
func newFederatedWorkspaceRole(workspaceRole *iamv1alpha2.WorkspaceRole) (*typesv1beta1.FederatedWorkspaceRole, error) {
|
||||
federatedWorkspaceRole := &typesv1beta1.FederatedWorkspaceRole{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: typesv1beta1.FederatedWorkspaceRoleKind,
|
||||
APIVersion: typesv1beta1.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: workspaceRole.Name,
|
||||
},
|
||||
@@ -206,7 +202,6 @@ func (r *Reconciler) ensureNotControlledByKubefed(ctx context.Context, logger lo
|
||||
if workspaceRole.Labels == nil {
|
||||
workspaceRole.Labels = make(map[string]string)
|
||||
}
|
||||
workspaceRole = workspaceRole.DeepCopy()
|
||||
workspaceRole.Labels[constants.KubefedManagedLabel] = "false"
|
||||
if err := r.Update(ctx, workspaceRole); err != nil {
|
||||
logger.Error(err, "update kubefed managed label failed")
|
||||
|
||||
@@ -123,7 +123,6 @@ func (r *Reconciler) bindWorkspace(ctx context.Context, logger logr.Logger, work
|
||||
}
|
||||
// owner reference not match workspace label
|
||||
if !metav1.IsControlledBy(workspaceRoleBinding, workspace) {
|
||||
workspaceRoleBinding := workspaceRoleBinding.DeepCopy()
|
||||
workspaceRoleBinding.OwnerReferences = k8sutil.RemoveWorkspaceOwnerReference(workspaceRoleBinding.OwnerReferences)
|
||||
if err := controllerutil.SetControllerReference(workspace, workspaceRoleBinding, r.Scheme); err != nil {
|
||||
logger.Error(err, "set controller reference failed")
|
||||
@@ -145,7 +144,7 @@ func (r *Reconciler) multiClusterSync(ctx context.Context, logger logr.Logger, w
|
||||
federatedWorkspaceRoleBinding := &typesv1beta1.FederatedWorkspaceRoleBinding{}
|
||||
if err := r.Client.Get(ctx, types.NamespacedName{Name: workspaceRoleBinding.Name}, federatedWorkspaceRoleBinding); err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
if federatedWorkspaceRoleBinding, err := newFederatedWorkspaceRole(workspaceRoleBinding); err != nil {
|
||||
if federatedWorkspaceRoleBinding, err := newFederatedWorkspaceRoleBinding(workspaceRoleBinding); err != nil {
|
||||
logger.Error(err, "generate federated workspace role binding failed")
|
||||
return err
|
||||
} else {
|
||||
@@ -153,6 +152,7 @@ func (r *Reconciler) multiClusterSync(ctx context.Context, logger logr.Logger, w
|
||||
logger.Error(err, "create federated workspace role binding failed")
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
logger.Error(err, "get federated workspace role binding failed")
|
||||
@@ -176,12 +176,8 @@ func (r *Reconciler) multiClusterSync(ctx context.Context, logger logr.Logger, w
|
||||
return nil
|
||||
}
|
||||
|
||||
func newFederatedWorkspaceRole(workspaceRoleBinding *iamv1alpha2.WorkspaceRoleBinding) (*typesv1beta1.FederatedWorkspaceRoleBinding, error) {
|
||||
federatedWorkspaceRole := &typesv1beta1.FederatedWorkspaceRoleBinding{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: typesv1beta1.FederatedWorkspaceRoleBindingKind,
|
||||
APIVersion: typesv1beta1.SchemeGroupVersion.String(),
|
||||
},
|
||||
func newFederatedWorkspaceRoleBinding(workspaceRoleBinding *iamv1alpha2.WorkspaceRoleBinding) (*typesv1beta1.FederatedWorkspaceRoleBinding, error) {
|
||||
federatedWorkspaceRoleBinding := &typesv1beta1.FederatedWorkspaceRoleBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: workspaceRoleBinding.Name,
|
||||
},
|
||||
@@ -198,10 +194,10 @@ func newFederatedWorkspaceRole(workspaceRoleBinding *iamv1alpha2.WorkspaceRoleBi
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := controllerutil.SetControllerReference(workspaceRoleBinding, federatedWorkspaceRole, scheme.Scheme); err != nil {
|
||||
if err := controllerutil.SetControllerReference(workspaceRoleBinding, federatedWorkspaceRoleBinding, scheme.Scheme); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return federatedWorkspaceRole, nil
|
||||
return federatedWorkspaceRoleBinding, nil
|
||||
}
|
||||
|
||||
func (r *Reconciler) ensureNotControlledByKubefed(ctx context.Context, logger logr.Logger, workspaceRoleBinding *iamv1alpha2.WorkspaceRoleBinding) error {
|
||||
@@ -209,7 +205,6 @@ func (r *Reconciler) ensureNotControlledByKubefed(ctx context.Context, logger lo
|
||||
if workspaceRoleBinding.Labels == nil {
|
||||
workspaceRoleBinding.Labels = make(map[string]string)
|
||||
}
|
||||
workspaceRoleBinding = workspaceRoleBinding.DeepCopy()
|
||||
workspaceRoleBinding.Labels[constants.KubefedManagedLabel] = "false"
|
||||
logger.V(4).Info("update kubefed managed label")
|
||||
if err := r.Update(ctx, workspaceRoleBinding); err != nil {
|
||||
|
||||
@@ -91,9 +91,8 @@ func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
// +kubebuilder:rbac:groups=tenant.kubesphere.io,resources=workspaces,verbs=get;list;watch;
|
||||
func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
logger := r.Logger.WithValues("workspacetemplate", req.NamespacedName)
|
||||
rootCtx := context.Background()
|
||||
workspaceTemplate := &tenantv1alpha2.WorkspaceTemplate{}
|
||||
if err := r.Get(rootCtx, req.NamespacedName, workspaceTemplate); err != nil {
|
||||
if err := r.Get(ctx, req.NamespacedName, workspaceTemplate); err != nil {
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
@@ -102,7 +101,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||
// then lets add the finalizer and update the object.
|
||||
if !sliceutil.HasString(workspaceTemplate.ObjectMeta.Finalizers, workspaceTemplateFinalizer) {
|
||||
workspaceTemplate.ObjectMeta.Finalizers = append(workspaceTemplate.ObjectMeta.Finalizers, workspaceTemplateFinalizer)
|
||||
if err := r.Update(rootCtx, workspaceTemplate); err != nil {
|
||||
if err := r.Update(ctx, workspaceTemplate); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
@@ -110,16 +109,16 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||
// The object is being deleted
|
||||
if sliceutil.HasString(workspaceTemplate.ObjectMeta.Finalizers, workspaceTemplateFinalizer) ||
|
||||
sliceutil.HasString(workspaceTemplate.ObjectMeta.Finalizers, orphanFinalizer) {
|
||||
if err := r.deleteOpenPitrixResourcesInWorkspace(rootCtx, workspaceTemplate.Name); err != nil {
|
||||
logger.Error(err, "delete resource in workspace template failed")
|
||||
if err := r.deleteOpenPitrixResourcesInWorkspace(ctx, workspaceTemplate.Name); err != nil {
|
||||
logger.Error(err, "failed to delete related openpitrix resource")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
if err := r.deleteWorkspace(rootCtx, workspaceTemplate); err != nil {
|
||||
if err := r.deleteWorkspace(ctx, workspaceTemplate); err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
logger.V(4).Info("workspace not found", "workspacerole", workspaceTemplate.Name)
|
||||
logger.V(4).Info("related workspace not found")
|
||||
} else {
|
||||
logger.Error(err, "failed delete workspaces")
|
||||
logger.Error(err, "failed to delete related workspace")
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
}
|
||||
@@ -130,7 +129,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||
})
|
||||
|
||||
logger.V(4).Info("update workspace template")
|
||||
if err := r.Update(rootCtx, workspaceTemplate); err != nil {
|
||||
if err := r.Update(ctx, workspaceTemplate); err != nil {
|
||||
logger.Error(err, "update workspace template failed")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
@@ -140,18 +139,18 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
|
||||
}
|
||||
|
||||
if r.MultiClusterEnabled {
|
||||
if err := r.multiClusterSync(rootCtx, logger, workspaceTemplate); err != nil {
|
||||
if err := r.multiClusterSync(ctx, logger, workspaceTemplate); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
} else {
|
||||
if err := r.singleClusterSync(rootCtx, logger, workspaceTemplate); err != nil {
|
||||
if err := r.singleClusterSync(ctx, logger, workspaceTemplate); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
if err := r.initWorkspaceRoles(rootCtx, logger, workspaceTemplate); err != nil {
|
||||
if err := r.initWorkspaceRoles(ctx, logger, workspaceTemplate); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
if err := r.initManagerRoleBinding(rootCtx, logger, workspaceTemplate); err != nil {
|
||||
if err := r.initManagerRoleBinding(ctx, logger, workspaceTemplate); err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
r.Recorder.Event(workspaceTemplate, corev1.EventTypeNormal, controllerutils.SuccessSynced, controllerutils.MessageResourceSynced)
|
||||
@@ -230,10 +229,6 @@ func (r *Reconciler) multiClusterSync(ctx context.Context, logger logr.Logger, w
|
||||
|
||||
func newFederatedWorkspace(template *tenantv1alpha2.WorkspaceTemplate) (*typesv1beta1.FederatedWorkspace, error) {
|
||||
federatedWorkspace := &typesv1beta1.FederatedWorkspace{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: typesv1beta1.FederatedWorkspaceRoleKind,
|
||||
APIVersion: typesv1beta1.SchemeGroupVersion.String(),
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: template.Name,
|
||||
Labels: template.Labels,
|
||||
@@ -261,6 +256,7 @@ func (r *Reconciler) deleteWorkspace(ctx context.Context, template *tenantv1alph
|
||||
if err := r.Client.Get(ctx, types.NamespacedName{Name: template.Name}, federatedWorkspace); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Workspace will be deleted with Orphan Option when it has a orphan finalizer.
|
||||
// Reousrces that owned by the Workspace will not be deleted.
|
||||
if sliceutil.HasString(template.ObjectMeta.Finalizers, orphanFinalizer) {
|
||||
@@ -271,7 +267,17 @@ func (r *Reconciler) deleteWorkspace(ctx context.Context, template *tenantv1alph
|
||||
if err := r.Update(ctx, federatedWorkspace); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Usually namespace will bind the lifecycle of workspace with ownerReference,
|
||||
// in multi-cluster environment workspace will not be created in host cluster
|
||||
// if the cluster is not be granted or kubefed-controller-manager is unavailable,
|
||||
// this will cause the federated namespace left an orphan object in host cluster.
|
||||
// After workspaceTemplate deleted we need to deleted orphan namespace in host cluster directly.
|
||||
if err := r.deleteNamespacesInWorkspace(ctx, template); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := r.Delete(ctx, federatedWorkspace); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -301,7 +307,6 @@ func (r *Reconciler) ensureNotControlledByKubefed(ctx context.Context, logger lo
|
||||
if workspaceTemplate.Labels == nil {
|
||||
workspaceTemplate.Labels = make(map[string]string)
|
||||
}
|
||||
workspaceTemplate = workspaceTemplate.DeepCopy()
|
||||
workspaceTemplate.Labels[constants.KubefedManagedLabel] = "false"
|
||||
logger.V(4).Info("update kubefed managed label")
|
||||
if err := r.Update(ctx, workspaceTemplate); err != nil {
|
||||
@@ -326,8 +331,8 @@ func (r *Reconciler) initWorkspaceRoles(ctx context.Context, logger logr.Logger,
|
||||
expected.Labels = make(map[string]string)
|
||||
}
|
||||
expected.Labels[tenantv1alpha1.WorkspaceLabel] = workspace.Name
|
||||
var existed iamv1alpha2.WorkspaceRole
|
||||
if err := r.Get(ctx, types.NamespacedName{Name: expected.Name}, &existed); err != nil {
|
||||
workspaceRole := &iamv1alpha2.WorkspaceRole{}
|
||||
if err := r.Get(ctx, types.NamespacedName{Name: expected.Name}, workspaceRole); err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
logger.V(4).Info("create workspace role", "workspacerole", expected.Name)
|
||||
if err := r.Create(ctx, &expected); err != nil {
|
||||
@@ -340,15 +345,14 @@ func (r *Reconciler) initWorkspaceRoles(ctx context.Context, logger logr.Logger,
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !reflect.DeepEqual(expected.Labels, existed.Labels) ||
|
||||
!reflect.DeepEqual(expected.Annotations, existed.Annotations) ||
|
||||
!reflect.DeepEqual(expected.Rules, existed.Rules) {
|
||||
updated := existed.DeepCopy()
|
||||
updated.Labels = expected.Labels
|
||||
updated.Annotations = expected.Annotations
|
||||
updated.Rules = expected.Rules
|
||||
logger.V(4).Info("update workspace role", "workspacerole", updated.Name)
|
||||
if err := r.Update(ctx, updated); err != nil {
|
||||
if !reflect.DeepEqual(expected.Labels, workspaceRole.Labels) ||
|
||||
!reflect.DeepEqual(expected.Annotations, workspaceRole.Annotations) ||
|
||||
!reflect.DeepEqual(expected.Rules, workspaceRole.Rules) {
|
||||
workspaceRole.Labels = expected.Labels
|
||||
workspaceRole.Annotations = expected.Annotations
|
||||
workspaceRole.Rules = expected.Rules
|
||||
logger.V(4).Info("update workspace role", "workspacerole", workspaceRole.Name)
|
||||
if err := r.Update(ctx, workspaceRole); err != nil {
|
||||
logger.Error(err, "update workspace role failed")
|
||||
return err
|
||||
}
|
||||
@@ -471,6 +475,12 @@ func (r *Reconciler) deleteHelmRepos(ctx context.Context, ws string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// deleteNamespacesInWorkspace Deletes the namespace associated with the workspace, which match the workspace label selector
|
||||
func (r *Reconciler) deleteNamespacesInWorkspace(ctx context.Context, template *tenantv1alpha2.WorkspaceTemplate) error {
|
||||
namespace := &corev1.Namespace{}
|
||||
return r.DeleteAllOf(ctx, namespace, client.MatchingLabels{tenantv1alpha1.WorkspaceLabel: template.Name})
|
||||
}
|
||||
|
||||
func workspaceRoleBindingChanger(workspaceRoleBinding *iamv1alpha2.WorkspaceRoleBinding, workspace, username, workspaceRoleName string) controllerutil.MutateFn {
|
||||
return func() error {
|
||||
workspaceRoleBinding.Labels = map[string]string{
|
||||
|
||||
@@ -227,15 +227,30 @@ func (h *handler) PodLogSearch(request *restful.Request, response *restful.Respo
|
||||
}
|
||||
|
||||
noHit := len(namespaceCreateTimeMap) == 0 || len(podfilter) == 0
|
||||
if noHit {
|
||||
ar.Logs = &loggingclient.Logs{}
|
||||
|
||||
if logQuery.Operation == loggingv1alpha2.OperationExport {
|
||||
response.Header().Set(restful.HEADER_ContentType, "text/plain")
|
||||
response.Header().Set("Content-Disposition", "attachment")
|
||||
if noHit {
|
||||
return
|
||||
}
|
||||
|
||||
err = h.lo.ExportLogs(sf, response)
|
||||
if err != nil {
|
||||
api.HandleInternalError(response, request, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if noHit {
|
||||
ar.Logs = &loggingclient.Logs{}
|
||||
}
|
||||
|
||||
ar, err = h.lo.SearchLogs(sf, logQuery.From, logQuery.Size, logQuery.Sort)
|
||||
if err != nil {
|
||||
api.HandleError(response, request, err)
|
||||
return
|
||||
}
|
||||
response.WriteEntity(ar)
|
||||
}
|
||||
|
||||
response.WriteEntity(ar)
|
||||
}
|
||||
|
||||
@@ -259,12 +259,12 @@ func (c *gatewayOperator) GetGateways(namespace string) ([]*v1alpha1.Gateway, er
|
||||
}
|
||||
obj := &v1alpha1.Gateway{}
|
||||
err := c.client.Get(context.TODO(), key, obj)
|
||||
if errors.IsNotFound(err) {
|
||||
return gateways, nil
|
||||
} else if err != nil {
|
||||
|
||||
if err == nil {
|
||||
gateways = append(gateways, obj)
|
||||
} else if err != nil && !errors.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
gateways = append(gateways, obj)
|
||||
|
||||
for _, g := range gateways {
|
||||
s := &corev1.Service{}
|
||||
@@ -281,7 +281,7 @@ func (c *gatewayOperator) GetGateways(namespace string) ([]*v1alpha1.Gateway, er
|
||||
}
|
||||
}
|
||||
|
||||
return gateways, err
|
||||
return gateways, nil
|
||||
}
|
||||
|
||||
// Create a Gateway in a namespace
|
||||
@@ -457,8 +457,10 @@ func (c *gatewayOperator) filter(object runtime.Object, filter query.Filter) boo
|
||||
return false
|
||||
}
|
||||
namesapce = svc.Labels["project"]
|
||||
objMeta = svc.ObjectMeta
|
||||
} else {
|
||||
namesapce = gateway.Spec.Conroller.Scope.Namespace
|
||||
objMeta = gateway.ObjectMeta
|
||||
}
|
||||
|
||||
switch filter.Field {
|
||||
|
||||
@@ -182,6 +182,9 @@ func Test_gatewayOperator_GetGateways(t *testing.T) {
|
||||
Type: corev1.ServiceTypeNodePort,
|
||||
},
|
||||
},
|
||||
Status: runtime.RawExtension{
|
||||
Raw: []byte("{\"loadBalancer\":{},\"service\":[{\"name\":\"http\",\"protocol\":\"TCP\",\"port\":80,\"targetPort\":0}]}\n"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -167,8 +167,8 @@ var promQLTemplates = map[string]string{
|
||||
|
||||
// ingress
|
||||
"ingress_request_count": `round(sum(increase(nginx_ingress_controller_requests{$1,$2}[$3])))`,
|
||||
"ingress_request_4xx_count": `round(sum(increase(nginx_ingress_controller_requests{$1,$2,status="[4].*"}[$3])))`,
|
||||
"ingress_request_5xx_count": `round(sum(increase(nginx_ingress_controller_requests{$1,$2,status="[5].*"}[$3])))`,
|
||||
"ingress_request_4xx_count": `round(sum(increase(nginx_ingress_controller_requests{$1,$2,status=~"[4].*"}[$3])))`,
|
||||
"ingress_request_5xx_count": `round(sum(increase(nginx_ingress_controller_requests{$1,$2,status=~"[5].*"}[$3])))`,
|
||||
"ingress_active_connections": `sum(avg_over_time(nginx_ingress_controller_nginx_process_connections{$2,state="active"}[$3]))`,
|
||||
"ingress_success_rate": `sum(rate(nginx_ingress_controller_requests{$1,$2,status!~"[4-5].*"}[$3])) / sum(rate(nginx_ingress_controller_requests{$1,$2}[$3]))`,
|
||||
"ingress_request_duration_average": `sum_over_time(nginx_ingress_controller_request_duration_seconds_sum{$1,$2}[$3])/sum_over_time(nginx_ingress_controller_request_duration_seconds_count{$1,$2}[$3])`,
|
||||
|
||||
@@ -16,32 +16,7 @@ limitations under the License.
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 0 is considered as a non valid port
|
||||
func IsValidPort(port int) bool {
|
||||
return port > 0 && port < 65535
|
||||
}
|
||||
|
||||
func GetRequestIP(req *http.Request) string {
|
||||
address := strings.Trim(req.Header.Get("X-Real-Ip"), " ")
|
||||
if address != "" {
|
||||
return address
|
||||
}
|
||||
|
||||
address = strings.Trim(req.Header.Get("X-Forwarded-For"), " ")
|
||||
if address != "" {
|
||||
return address
|
||||
}
|
||||
|
||||
address, _, err := net.SplitHostPort(req.RemoteAddr)
|
||||
if err != nil {
|
||||
return req.RemoteAddr
|
||||
}
|
||||
|
||||
return address
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user