Repository: crossplane-contrib/provider-gitlab
Pull request #89 (2023-08-08 14:25:59)

fix(group): add nil check before ParentID dereferencing in ResolveReferences()
### Description of your changes
add nil check for `ParentID` in ResolveReferences() for group. 

I have:
- [x] Read and followed Crossplane's [contribution process].
- [x] Run `make reviewable test` to ensure this PR is ready for review.




Patch:
diff --git a/apis/groups/v1alpha1/referencers.go b/apis/groups/v1alpha1/referencers.go
index 7123953..6d4ca74 100644
--- a/apis/groups/v1alpha1/referencers.go
+++ b/apis/groups/v1alpha1/referencers.go
@@ -100,10 +100,14 @@ func (mg *Group) ResolveReferences(ctx context.Context, c client.Reader) error {
 	var rsp reference.ResolutionResponse
 	var err error
 
-	idstr := strconv.Itoa(*mg.Spec.ForProvider.ParentID)
+	var idstrp *string
+	if mg.Spec.ForProvider.ParentID != nil {
+		str := strconv.Itoa(*mg.Spec.ForProvider.ParentID)
+		idstrp = &str
+	}
 
 	rsp, err = r.Resolve(ctx, reference.ResolutionRequest{
-		CurrentValue: reference.FromPtrValue(&idstr),
+		CurrentValue: reference.FromPtrValue(idstrp),
 		Extract:      reference.ExternalName(),
 		Reference:    mg.Spec.ForProvider.ParentIDRef,
 		Selector:     mg.Spec.ForProvider.ParentIDSelector,
@@ -125,7 +129,7 @@ func (mg *Group) ResolveReferences(ctx context.Context, c client.Reader) error {
 	mg.Spec.ForProvider.ParentIDRef = rsp.ResolvedReference
 
 	for i3 := 0; i3 < len(mg.Spec.ForProvider.SharedWithGroups); i3++ {
-		idstr = strconv.Itoa(*mg.Spec.ForProvider.SharedWithGroups[i3].GroupID)
+		idstr := strconv.Itoa(*mg.Spec.ForProvider.SharedWithGroups[i3].GroupID)
 		rsp, err = r.Resolve(ctx, reference.ResolutionRequest{
 			CurrentValue: reference.FromPtrValue(&idstr),
 			Extract:      reference.ExternalName(),
diff --git a/pkg/clients/groups/deploytoken.go b/pkg/clients/groups/deploytoken.go
index 9ef2d04..14f4cc7 100644
--- a/pkg/clients/groups/deploytoken.go
+++ b/pkg/clients/groups/deploytoken.go
@@ -27,7 +27,7 @@ import (
 
 // DeployTokenClient defines Gitlab Group service operations
 type DeployTokenClient interface {
-	GetGroupDeployToken(gid interface{}, deployToken int, options ...gitlab.RequestOptionFunc) (*gitlab.DeployToken, *gitlab.Response, error)
+	ListGroupDeployTokens(gid interface{}, opt *gitlab.ListGroupDeployTokensOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.DeployToken, *gitlab.Response, error)
 	CreateGroupDeployToken(gid interface{}, opt *gitlab.CreateGroupDeployTokenOptions, options ...gitlab.RequestOptionFunc) (*gitlab.DeployToken, *gitlab.Response, error)
 	DeleteGroupDeployToken(gid interface{}, deployToken int, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error)
 }
diff --git a/pkg/clients/groups/fake/fake.go b/pkg/clients/groups/fake/fake.go
index 87a4bbf..aef2f89 100644
--- a/pkg/clients/groups/fake/fake.go
+++ b/pkg/clients/groups/fake/fake.go
@@ -40,9 +40,9 @@ type MockClient struct {
 	MockEditMember   func(gid interface{}, user int, opt *gitlab.EditGroupMemberOptions, options ...gitlab.RequestOptionFunc) (*gitlab.GroupMember, *gitlab.Response, error)
 	MockRemoveMember func(gid interface{}, user int, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error)
 
-	MockGetGroupDeployToken    func(gid interface{}, deployToken int, options ...gitlab.RequestOptionFunc) (*gitlab.DeployToken, *gitlab.Response, error)
-	MockCreateGroupDeployToken func(gid interface{}, opt *gitlab.CreateGroupDeployTokenOptions, options ...gitlab.RequestOptionFunc) (*gitlab.DeployToken, *gitlab.Response, error)
-	MockDeleteGroupDeployToken func(gid interface{}, deployToken int, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error)
+	MockListDeployTokens  func(gid interface{}, opt *gitlab.ListGroupDeployTokensOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.DeployToken, *gitlab.Response, error)
+	MockCreateDeployToken func(gid interface{}, opt *gitlab.CreateGroupDeployTokenOptions, options ...gitlab.RequestOptionFunc) (*gitlab.DeployToken, *gitlab.Response, error)
+	MockDeleteDeployToken func(gid interface{}, deployToken int, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error)
 }
 
 // GetGroup calls the underlying MockGetGroup method.
@@ -95,17 +95,17 @@ func (c *MockClient) RemoveGroupMember(gid interface{}, user int, opt *gitlab.Re
 	return c.MockRemoveMember(gid, user)
 }
 
-// GetGroupDeployToken calls the underlying MockGetGroupDeployToken method.
-func (c *MockClient) GetGroupDeployToken(gid interface{}, deployToken int, options ...gitlab.RequestOptionFunc) (*gitlab.DeployToken, *gitlab.Response, error) {
-	return c.MockGetGroupDeployToken(gid, deployToken)
+// ListGroupDeployTokens calls the underlying MockListGroupDeployTokens method.
+func (c *MockClient) ListGroupDeployTokens(gid interface{}, opt *gitlab.ListGroupDeployTokensOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.DeployToken, *gitlab.Response, error) {
+	return c.MockListDeployTokens(gid, opt)
 }
 
 // CreateGroupDeployToken calls the underlying MockCreateGroupDeployToken method.
 func (c *MockClient) CreateGroupDeployToken(gid interface{}, opt *gitlab.CreateGroupDeployTokenOptions, options ...gitlab.RequestOptionFunc) (*gitlab.DeployToken, *gitlab.Response, error) {
-	return c.MockCreateGroupDeployToken(gid, opt)
+	return c.MockCreateDeployToken(gid, opt)
 }
 
 // DeleteGroupDeployToken calls the underlying MockDeleteGroupDeployToken method.
 func (c *MockClient) DeleteGroupDeployToken(gid interface{}, deployToken int, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error) {
-	return c.MockDeleteGroupDeployToken(gid, deployToken)
+	return c.MockDeleteDeployToken(gid, deployToken)
 }
diff --git a/pkg/clients/projects/fake/fake.go b/pkg/clients/projects/fake/fake.go
index e0713c5..898f431 100644
--- a/pkg/clients/projects/fake/fake.go
+++ b/pkg/clients/projects/fake/fake.go
@@ -53,9 +53,9 @@ type MockClient struct {
 	MockListVariables  func(pid interface{}, opt *gitlab.ListProjectVariablesOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.ProjectVariable, *gitlab.Response, error)
 	MockRemoveVariable func(pid interface{}, key string, opt *gitlab.RemoveProjectVariableOptions, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error)
 
-	MockGetProjectAccessToken    func(pid interface{}, id int, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectAccessToken, *gitlab.Response, error)
-	MockCreateProjectAccessToken func(pid interface{}, opt *gitlab.CreateProjectAccessTokenOptions, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectAccessToken, *gitlab.Response, error)
-	MockRevokeProjectAccessToken func(pid interface{}, id int, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error)
+	MockGetAccessTokens   func(pid interface{}, id int, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectAccessToken, *gitlab.Response, error)
+	MockCreateAccessToken func(pid interface{}, opt *gitlab.CreateProjectAccessTokenOptions, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectAccessToken, *gitlab.Response, error)
+	MockRevokeAccessToken func(pid interface{}, id int, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error)
 
 	MockAddDeployKey    func(pid interface{}, opt *gitlab.AddDeployKeyOptions, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectDeployKey, *gitlab.Response, error)
 	MockDeleteDeployKey func(pid interface{}, deployKey int, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error)
@@ -231,17 +231,17 @@ func (c *MockClient) UpdateDeployKey(pid interface{}, deployKey int, opt *gitlab
 	return c.MockUpdateDeployKey(pid, deployKey, opt)
 }
 
-// GetProjectAccessToken calls the underlying MockGetProjectAccessToken method.
+// GetProjectAccessToken calls the underlying MockGetAccessTokens method.
 func (c *MockClient) GetProjectAccessToken(pid interface{}, id int, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectAccessToken, *gitlab.Response, error) {
-	return c.MockGetProjectAccessToken(pid, id)
+	return c.MockGetAccessTokens(pid, id)
 }
 
 // CreateProjectAccessToken calls the underlying MockCreateProjectAccessToken method.
 func (c *MockClient) CreateProjectAccessToken(pid interface{}, opt *gitlab.CreateProjectAccessTokenOptions, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectAccessToken, *gitlab.Response, error) {
-	return c.MockCreateProjectAccessToken(pid, opt)
+	return c.MockCreateAccessToken(pid, opt)
 }
 
-// RevokeProjectAccessToken calls the underlying MockRevokeProjectAccessToken method.
+// RevokeProjectAccessToken calls the underlying MockDeleteProjectAccessToken method.
 func (c *MockClient) RevokeProjectAccessToken(pid interface{}, id int, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error) {
-	return c.MockRevokeProjectAccessToken(pid, id)
+	return c.MockRevokeAccessToken(pid, id)
 }
diff --git a/pkg/controller/groups/deploytokens/controller.go b/pkg/controller/groups/deploytokens/controller.go
index 821999e..5c36ef0 100644
--- a/pkg/controller/groups/deploytokens/controller.go
+++ b/pkg/controller/groups/deploytokens/controller.go
@@ -45,8 +45,6 @@ const (
 	errGetFailed      = "cannot get Gitlab deploytoken"
 	errCreateFailed   = "cannot create Gitlab deploytoken"
 	errDeleteFailed   = "cannot delete Gitlab deploytoken"
-	errIDNotInt       = "ID is not integer value"
-	errGroupIDMissing = "GroupID is missing"
 )
 
 // SetupDeployToken adds a controller that reconciles GroupDeployTokens.
@@ -97,21 +95,21 @@ func (e *external) Observe(ctx context.Context, mg resource.Managed) (managed.Ex
 		return managed.ExternalObservation{ResourceExists: false}, nil
 	}
 
-	id, err := strconv.Atoi(externalName)
+	groupDeployTokenID, err := strconv.Atoi(externalName)
 	if err != nil {
-		return managed.ExternalObservation{}, errors.New(errIDNotInt)
+		return managed.ExternalObservation{}, errors.New(errNotDeployToken)
 	}
 
-	if cr.Spec.ForProvider.GroupID == nil {
-		return managed.ExternalObservation{}, errors.New(errGroupIDMissing)
+	listGroupDeployTokensOptions := gitlab.ListGroupDeployTokensOptions{}
+	deploytokenArr, _, err := e.client.ListGroupDeployTokens(*cr.Spec.ForProvider.GroupID, &listGroupDeployTokensOptions)
+	if err != nil {
+		return managed.ExternalObservation{}, errors.Wrap(resource.Ignore(groups.IsErrorGroupDeployTokenNotFound, err), errGetFailed)
 	}
 
-	dt, res, err := e.client.GetGroupDeployToken(*cr.Spec.ForProvider.GroupID, id)
-	if err != nil {
-		if clients.IsResponseNotFound(res) {
-			return managed.ExternalObservation{}, nil
-		}
-		return managed.ExternalObservation{}, errors.Wrap(err, errGetFailed)
+	dt := findDeployToken(groupDeployTokenID, deploytokenArr)
+
+	if dt == nil {
+		return managed.ExternalObservation{}, nil
 	}
 
 	current := cr.Spec.ForProvider.DeepCopy()
@@ -195,3 +193,14 @@ func lateInitializeGroupDeployToken(in *v1alpha1.DeployTokenParameters, deployTo
 		in.ExpiresAt = &metav1.Time{Time: *deployToken.ExpiresAt}
 	}
 }
+
+// findDeployToken try to find a deploy token with the ID in the deploy token array,
+// if found return a deploy token otherwise return nil.
+func findDeployToken(deployTokenID int, deployTokens []*gitlab.DeployToken) *gitlab.DeployToken {
+	for _, v := range deployTokens {
+		if v.ID == deployTokenID {
+			return v
+		}
+	}
+	return nil
+}
diff --git a/pkg/controller/groups/group.go b/pkg/controller/groups/group.go
index ae0389c..120bdbe 100644
--- a/pkg/controller/groups/group.go
+++ b/pkg/controller/groups/group.go
@@ -103,15 +103,12 @@ func (e *external) Observe(ctx context.Context, mg resource.Managed) (managed.Ex
 
 	groupID, err := strconv.Atoi(externalName)
 	if err != nil {
-		return managed.ExternalObservation{}, errors.New(errIDNotInt)
+		return managed.ExternalObservation{}, errors.New(errNotGroup)
 	}
 
-	grp, res, err := e.client.GetGroup(groupID, nil)
+	grp, _, err := e.client.GetGroup(groupID, nil)
 	if err != nil {
-		if clients.IsResponseNotFound(res) {
-			return managed.ExternalObservation{}, nil
-		}
-		return managed.ExternalObservation{}, errors.Wrap(err, errGetFailed)
+		return managed.ExternalObservation{}, errors.Wrap(resource.Ignore(groups.IsErrorGroupNotFound, err), errGetFailed)
 	}
 
 	current := cr.Spec.ForProvider.DeepCopy()
diff --git a/pkg/controller/groups/members/controller.go b/pkg/controller/groups/members/controller.go
index 35875f7..a499c21 100644
--- a/pkg/controller/groups/members/controller.go
+++ b/pkg/controller/groups/members/controller.go
@@ -38,11 +38,9 @@ import (
 
 const (
 	errNotMember    = "managed resource is not a Gitlab Group Member custom resource"
-	errIDNotInt     = "ID is not an integer value"
 	errCreateFailed = "cannot create Gitlab Group Member"
 	errUpdateFailed = "cannot update Gitlab Group Member"
 	errDeleteFailed = "cannot delete Gitlab Group Member"
-	errGetFailed    = "cannot get Gitlab Group Member"
 )
 
 // SetupMember adds a controller that reconciles Group Members.
@@ -95,15 +93,12 @@ func (e *external) Observe(ctx context.Context, mg resource.Managed) (managed.Ex
 
 	groupID, err := strconv.Atoi(externalName)
 	if err != nil {
-		return managed.ExternalObservation{}, errors.New(errIDNotInt)
+		return managed.ExternalObservation{}, errors.New(errNotMember)
 	}
 
-	groupMember, res, err := e.client.GetGroupMember(groupID, cr.Spec.ForProvider.UserID)
-	if err != nil {
-		if clients.IsResponseNotFound(res) {
-			return managed.ExternalObservation{}, nil
-		}
-		return managed.ExternalObservation{}, errors.Wrap(err, errGetFailed)
+	groupMember, _, _ := e.client.GetGroupMember(groupID, cr.Spec.ForProvider.UserID)
+	if groupMember == nil {
+		return managed.ExternalObservation{ResourceExists: false}, nil
 	}
 
 	cr.Status.AtProvider = groups.GenerateMemberObservation(groupMember)
diff --git a/pkg/controller/projects/accesstokens/controller.go b/pkg/controller/projects/accesstokens/controller.go
index f8c3940..8e3af3f 100644
--- a/pkg/controller/projects/accesstokens/controller.go
+++ b/pkg/controller/projects/accesstokens/controller.go
@@ -109,11 +109,8 @@ func (e *external) Observe(ctx context.Context, mg resource.Managed) (managed.Ex
 		return managed.ExternalObservation{}, errors.New(errMissingProjectID)
 	}
 
-	at, res, err := e.client.GetProjectAccessToken(*cr.Spec.ForProvider.ProjectID, accessTokenID)
+	at, _, err := e.client.GetProjectAccessToken(*cr.Spec.ForProvider.ProjectID, accessTokenID)
 	if err != nil {
-		if clients.IsResponseNotFound(res) {
-			return managed.ExternalObservation{}, nil
-		}
 		return managed.ExternalObservation{}, errors.Wrap(err, errAccessTokentNotFound)
 	}
 
diff --git a/pkg/controller/projects/deploykeys/controller.go b/pkg/controller/projects/deploykeys/controller.go
index a5bbfe8..105282b 100644
--- a/pkg/controller/projects/deploykeys/controller.go
+++ b/pkg/controller/projects/deploykeys/controller.go
@@ -3,6 +3,7 @@ package deploykeys
 import (
 	"context"
 	"strconv"
+	"strings"
 
 	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
 	"github.com/crossplane/crossplane-runtime/pkg/event"
@@ -100,16 +101,18 @@ func (e *external) Observe(ctx context.Context, mg resource.Managed) (managed.Ex
 		return managed.ExternalObservation{}, errors.New(errIDNotAnInt)
 	}
 
-	dk, res, err := e.client.GetDeployKey(
+	dk, _, err := e.client.GetDeployKey(
 		*cr.Spec.ForProvider.ProjectID,
 		id,
 	)
 
 	if err != nil {
-		if clients.IsResponseNotFound(res) {
-			return managed.ExternalObservation{}, nil
-		}
-		return managed.ExternalObservation{}, errors.Wrap(err, errGetFail)
+		return managed.ExternalObservation{},
+			errors.Wrap(resource.Ignore(isErrorProjectDeployKeyNotFound, err), errGetFail)
+	}
+
+	if dk == nil {
+		return managed.ExternalObservation{}, nil
 	}
 
 	currentState := cr.Spec.ForProvider.DeepCopy()
@@ -226,6 +229,14 @@ func (e *external) Delete(ctx context.Context, mg resource.Managed) error {
 	return errors.Wrap(err, errDeleteFail)
 }
 
+func isErrorProjectDeployKeyNotFound(err error) bool {
+	if err != nil {
+		return false
+	}
+
+	return strings.Contains(err.Error(), errNotFound)
+}
+
 func lateInitializeProjectDeployKey(local *v1alpha1.DeployKeyParameters, external *gitlab.ProjectDeployKey) {
 	if external == nil {
 		return
diff --git a/pkg/controller/projects/deploytokens/controller.go b/pkg/controller/projects/deploytokens/controller.go
index 01f33a7..44ac087 100644
--- a/pkg/controller/projects/deploytokens/controller.go
+++ b/pkg/controller/projects/deploytokens/controller.go
@@ -106,15 +106,16 @@ func (e *external) Observe(ctx context.Context, mg resource.Managed) (managed.Ex
 		return managed.ExternalObservation{}, errors.New(errProjectIDMissing)
 	}
 
-	dt, res, err := e.client.GetProjectDeployToken(*cr.Spec.ForProvider.ProjectID, id)
+	dt, _, err := e.client.GetProjectDeployToken(*cr.Spec.ForProvider.ProjectID, id)
 
 	if err != nil {
-		if clients.IsResponseNotFound(res) {
-			return managed.ExternalObservation{}, nil
-		}
 		return managed.ExternalObservation{}, errors.Wrap(err, errGetFailed)
 	}
 
+	if dt == nil {
+		return managed.ExternalObservation{}, nil
+	}
+
 	current := cr.Spec.ForProvider.DeepCopy()
 	lateInitializeProjectDeployToken(&cr.Spec.ForProvider, dt)
 
diff --git a/pkg/controller/projects/hooks/controller.go b/pkg/controller/projects/hooks/controller.go
index bd4a542..e4103fa 100644
--- a/pkg/controller/projects/hooks/controller.go
+++ b/pkg/controller/projects/hooks/controller.go
@@ -102,11 +102,8 @@ func (e *external) Observe(ctx context.Context, mg resource.Managed) (managed.Ex
 		return managed.ExternalObservation{}, errors.New(errNotHook)
 	}
 
-	projecthook, res, err := e.client.GetProjectHook(*cr.Spec.ForProvider.ProjectID, hookid)
+	projecthook, _, err := e.client.GetProjectHook(*cr.Spec.ForProvider.ProjectID, hookid)
 	if err != nil {
-		if clients.IsResponseNotFound(res) {
-			return managed.ExternalObservation{}, nil
-		}
 		return managed.ExternalObservation{}, errors.Wrap(resource.Ignore(projects.IsErrorHookNotFound, err), errGetFailed)
 	}
 
diff --git a/pkg/controller/projects/members/controller.go b/pkg/controller/projects/members/controller.go
index 9fabafa..8e1d5fa 100644
--- a/pkg/controller/projects/members/controller.go
+++ b/pkg/controller/projects/members/controller.go
@@ -37,11 +37,10 @@ import (
 )
 
 const (
-	errNotMember     = "managed resource is not a Gitlab Project Member custom resource"
-	errCreateFailed  = "cannot create Gitlab Project Member"
-	errUpdateFailed  = "cannot update Gitlab Project Member"
-	errDeleteFailed  = "cannot delete Gitlab Project Member"
-	errObserveFailed = "cannot observe Gitlab Project Member"
+	errNotMember    = "managed resource is not a Gitlab Project Member custom resource"
+	errCreateFailed = "cannot create Gitlab Project Member"
+	errUpdateFailed = "cannot update Gitlab Project Member"
+	errDeleteFailed = "cannot delete Gitlab Project Member"
 )
 
 // SetupMember adds a controller that reconciles Project Members.
@@ -97,12 +96,9 @@ func (e *external) Observe(ctx context.Context, mg resource.Managed) (managed.Ex
 		return managed.ExternalObservation{}, errors.New(errNotMember)
 	}
 
-	projectMember, res, err := e.client.GetProjectMember(projectMemberID, cr.Spec.ForProvider.UserID)
-	if err != nil {
-		if clients.IsResponseNotFound(res) {
-			return managed.ExternalObservation{}, nil
-		}
-		return managed.ExternalObservation{}, errors.Wrap(err, errObserveFailed)
+	projectMember, _, _ := e.client.GetProjectMember(projectMemberID, cr.Spec.ForProvider.UserID)
+	if projectMember == nil {
+		return managed.ExternalObservation{ResourceExists: false}, nil
 	}
 
 	cr.Status.AtProvider = projects.GenerateMemberObservation(projectMember)
diff --git a/pkg/controller/projects/project.go b/pkg/controller/projects/project.go
index 819cb32..9791e1b 100644
--- a/pkg/controller/projects/project.go
+++ b/pkg/controller/projects/project.go
@@ -46,7 +46,6 @@ const (
 	errCreateFailed     = "cannot create Gitlab project"
 	errUpdateFailed     = "cannot update Gitlab project"
 	errDeleteFailed     = "cannot delete Gitlab project"
-	errGetFailed        = "cannot retrieve Gitlab project with"
 )
 
 // SetupProject adds a controller that reconciles Projects.
@@ -102,12 +101,9 @@ func (e *external) Observe(ctx context.Context, mg resource.Managed) (managed.Ex
 		return managed.ExternalObservation{}, errors.New(errNotProject)
 	}
 
-	prj, res, err := e.client.GetProject(projectID, nil)
-	if err != nil {
-		if clients.IsResponseNotFound(res) {
-			return managed.ExternalObservation{}, nil
-		}
-		return managed.ExternalObservation{}, errors.Wrap(err, errGetFailed)
+	prj, _, _ := e.client.GetProject(projectID, nil)
+	if prj == nil {
+		return managed.ExternalObservation{ResourceExists: false}, nil
 	}
 
 	current := cr.Spec.ForProvider.DeepCopy()
@@ -220,7 +216,7 @@ func lateInitialize(in *v1alpha1.ProjectParameters, project *gitlab.Project) { /
 	if in.MirrorTriggerBuilds == nil {
 		in.MirrorTriggerBuilds = &project.MirrorTriggerBuilds
 	}
-	if in.MirrorUserID == nil && project.MirrorUserID != 0 { // since project.MirrorUserID is non-nullable, value `0` treated as `not set`
+	if in.MirrorUserID == nil {
 		in.MirrorUserID = &project.MirrorUserID
 	}
 	if in.OnlyAllowMergeIfAllDiscussionsAreResolved == nil {
diff --git a/pkg/controller/projects/variables/controller.go b/pkg/controller/projects/variables/controller.go
index 3bbd5da..3d53ed3 100644
--- a/pkg/controller/projects/variables/controller.go
+++ b/pkg/controller/projects/variables/controller.go
@@ -90,12 +90,9 @@ func (e *external) Observe(ctx context.Context, mg resource.Managed) (managed.Ex
 	if !ok {
 		return managed.ExternalObservation{}, errors.New(errNotVariable)
 	}
-	variable, res, err := e.client.GetVariable(*cr.Spec.ForProvider.ProjectID, cr.Spec.ForProvider.Key, projects.GenerateGetVariableOptions(&cr.Spec.ForProvider), gitlab.WithContext(ctx))
+	variable, _, err := e.client.GetVariable(*cr.Spec.ForProvider.ProjectID, cr.Spec.ForProvider.Key, projects.GenerateGetVariableOptions(&cr.Spec.ForProvider), gitlab.WithContext(ctx))
 	if err != nil {
-		if clients.IsResponseNotFound(res) {
-			return managed.ExternalObservation{}, nil
-		}
-		return managed.ExternalObservation{}, errors.Wrap(err, errGetFailed)
+		return managed.ExternalObservation{}, errors.Wrap(resource.Ignore(projects.IsErrorVariableNotFound, err), errGetFailed)
 	}
 
 	if cr.Spec.ForProvider.ValueSecretRef != nil {


Test patch:
diff --git a/pkg/controller/groups/deploytokens/controller_test.go b/pkg/controller/groups/deploytokens/controller_test.go
index b834bff..c3d61f9 100644
--- a/pkg/controller/groups/deploytokens/controller_test.go
+++ b/pkg/controller/groups/deploytokens/controller_test.go
@@ -19,7 +19,6 @@ package deploytokens
 import (
 	"context"
 	"fmt"
-	"net/http"
 	"strconv"
 	"testing"
 	"time"
@@ -141,18 +140,23 @@ func TestObserve(t *testing.T) {
 		},
 		"NotIDExternalName": {
 			args: args{
+				deployToken: &fake.MockClient{
+					MockListDeployTokens: func(pid interface{}, opt *gitlab.ListGroupDeployTokensOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.DeployToken, *gitlab.Response, error) {
+						return []*gitlab.DeployToken{}, &gitlab.Response{}, nil
+					},
+				},
 				cr: deployToken(withExternalName("fr")),
 			},
 			want: want{
 				cr:  deployToken(withExternalName("fr")),
-				err: errors.New(errIDNotInt),
+				err: errors.New(errNotDeployToken),
 			},
 		},
 		"FailedGetRequest": {
 			args: args{
 				deployToken: &fake.MockClient{
-					MockGetGroupDeployToken: func(gid interface{}, deployToken int, options ...gitlab.RequestOptionFunc) (*gitlab.DeployToken, *gitlab.Response, error) {
-						return nil, &gitlab.Response{Response: &http.Response{StatusCode: 400}}, errBoom
+					MockListDeployTokens: func(pid interface{}, opt *gitlab.ListGroupDeployTokensOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.DeployToken, *gitlab.Response, error) {
+						return []*gitlab.DeployToken{}, &gitlab.Response{}, errBoom
 					},
 				},
 				cr: deployToken(
@@ -172,11 +176,11 @@ func TestObserve(t *testing.T) {
 				err: errors.Wrap(errBoom, errGetFailed),
 			},
 		},
-		"ErrGet404": {
+		"DeployTokenNotFound": {
 			args: args{
 				deployToken: &fake.MockClient{
-					MockGetGroupDeployToken: func(gid interface{}, deployToken int, options ...gitlab.RequestOptionFunc) (*gitlab.DeployToken, *gitlab.Response, error) {
-						return nil, &gitlab.Response{Response: &http.Response{StatusCode: 404}}, errBoom
+					MockListDeployTokens: func(pid interface{}, opt *gitlab.ListGroupDeployTokensOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.DeployToken, *gitlab.Response, error) {
+						return deployTokens, &gitlab.Response{}, nil
 					},
 				},
 				cr: deployToken(
@@ -202,14 +206,13 @@ func TestObserve(t *testing.T) {
 					ResourceUpToDate:        false,
 					ResourceLateInitialized: false,
 				},
-				err: nil,
 			},
 		},
 		"LateInitSuccess": {
 			args: args{
 				deployToken: &fake.MockClient{
-					MockGetGroupDeployToken: func(gid interface{}, deployToken int, options ...gitlab.RequestOptionFunc) (*gitlab.DeployToken, *gitlab.Response, error) {
-						return deployTokens[0], nil, nil
+					MockListDeployTokens: func(pid interface{}, opt *gitlab.ListGroupDeployTokensOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.DeployToken, *gitlab.Response, error) {
+						return deployTokens, &gitlab.Response{}, nil
 					},
 				},
 				cr: deployToken(
@@ -239,8 +242,8 @@ func TestObserve(t *testing.T) {
 		"SuccessfulAvailable": {
 			args: args{
 				deployToken: &fake.MockClient{
-					MockGetGroupDeployToken: func(gid interface{}, deployToken int, options ...gitlab.RequestOptionFunc) (*gitlab.DeployToken, *gitlab.Response, error) {
-						return deployTokens[0], nil, nil
+					MockListDeployTokens: func(pid interface{}, opt *gitlab.ListGroupDeployTokensOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.DeployToken, *gitlab.Response, error) {
+						return deployTokens, &gitlab.Response{}, nil
 					},
 				},
 				cr: deployToken(
@@ -315,7 +318,7 @@ func TestCreate(t *testing.T) {
 					MockUpdate: test.NewMockUpdateFn(nil),
 				},
 				deployToken: &fake.MockClient{
-					MockCreateGroupDeployToken: func(pid interface{}, opt *gitlab.CreateGroupDeployTokenOptions, options ...gitlab.RequestOptionFunc) (*gitlab.DeployToken, *gitlab.Response, error) {
+					MockCreateDeployToken: func(pid interface{}, opt *gitlab.CreateGroupDeployTokenOptions, options ...gitlab.RequestOptionFunc) (*gitlab.DeployToken, *gitlab.Response, error) {
 						return &deployTokenObj, &gitlab.Response{}, nil
 					},
 				},
@@ -342,7 +345,7 @@ func TestCreate(t *testing.T) {
 		"FailedCreation": {
 			args: args{
 				deployToken: &fake.MockClient{
-					MockCreateGroupDeployToken: func(pid interface{}, opt *gitlab.CreateGroupDeployTokenOptions, options ...gitlab.RequestOptionFunc) (*gitlab.DeployToken, *gitlab.Response, error) {
+					MockCreateDeployToken: func(pid interface{}, opt *gitlab.CreateGroupDeployTokenOptions, options ...gitlab.RequestOptionFunc) (*gitlab.DeployToken, *gitlab.Response, error) {
 						return &gitlab.DeployToken{}, &gitlab.Response{}, errBoom
 					},
 				},
@@ -442,7 +445,7 @@ func TestDelete(t *testing.T) {
 		"SuccessfulDeletion": {
 			args: args{
 				deployToken: &fake.MockClient{
-					MockDeleteGroupDeployToken: func(pid interface{}, deployToken int, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error) {
+					MockDeleteDeployToken: func(pid interface{}, deployToken int, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error) {
 						return &gitlab.Response{}, nil
 					},
 				},
@@ -465,7 +468,7 @@ func TestDelete(t *testing.T) {
 		"FailedDeletionErrNotDeployToken": {
 			args: args{
 				deployToken: &fake.MockClient{
-					MockDeleteGroupDeployToken: func(pid interface{}, deployToken int, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error) {
+					MockDeleteDeployToken: func(pid interface{}, deployToken int, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error) {
 						return &gitlab.Response{}, nil
 					},
 				},
@@ -489,7 +492,7 @@ func TestDelete(t *testing.T) {
 		"FailedDeletion": {
 			args: args{
 				deployToken: &fake.MockClient{
-					MockDeleteGroupDeployToken: func(pid interface{}, deployToken int, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error) {
+					MockDeleteDeployToken: func(pid interface{}, deployToken int, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error) {
 						return &gitlab.Response{}, errBoom
 					},
 				},
diff --git a/pkg/controller/groups/group_test.go b/pkg/controller/groups/group_test.go
index 2809842..9832970 100644
--- a/pkg/controller/groups/group_test.go
+++ b/pkg/controller/groups/group_test.go
@@ -18,7 +18,6 @@ package groups
 
 import (
 	"context"
-	"net/http"
 	"reflect"
 	"testing"
 	"time"
@@ -259,14 +258,14 @@ func TestObserve(t *testing.T) {
 			},
 			want: want{
 				cr:  group(withExternalName("fr")),
-				err: errors.New(errIDNotInt),
+				err: errors.New(errNotGroup),
 			},
 		},
 		"FailedGetRequest": {
 			args: args{
 				group: &fake.MockClient{
 					MockGetGroup: func(pid interface{}, options ...gitlab.RequestOptionFunc) (*gitlab.Group, *gitlab.Response, error) {
-						return &gitlab.Group{}, &gitlab.Response{Response: &http.Response{StatusCode: 400}}, errBoom
+						return &gitlab.Group{}, &gitlab.Response{}, errBoom
 					},
 				},
 				cr: group(withExternalName(extName)),
@@ -276,20 +275,6 @@ func TestObserve(t *testing.T) {
 				err: errors.Wrap(errBoom, errGetFailed),
 			},
 		},
-		"ErrGet404": {
-			args: args{
-				group: &fake.MockClient{
-					MockGetGroup: func(pid interface{}, options ...gitlab.RequestOptionFunc) (*gitlab.Group, *gitlab.Response, error) {
-						return &gitlab.Group{}, &gitlab.Response{Response: &http.Response{StatusCode: 404}}, errBoom
-					},
-				},
-				cr: group(withExternalName(extName)),
-			},
-			want: want{
-				cr:  group(withAnnotations(extNameAnnotation)),
-				err: nil,
-			},
-		},
 		"LateInitSuccess": {
 			args: args{
 				kube: &test.MockClient{
diff --git a/pkg/controller/groups/members/controller_test.go b/pkg/controller/groups/members/controller_test.go
index 2be025a..780fcaa 100644
--- a/pkg/controller/groups/members/controller_test.go
+++ b/pkg/controller/groups/members/controller_test.go
@@ -15,7 +15,6 @@ package members
 
 import (
 	"context"
-	"net/http"
 	"strconv"
 	"testing"
 	"time"
@@ -189,29 +188,14 @@ func TestObserve(t *testing.T) {
 			},
 			want: want{
 				cr:  groupMember(withExternalName("fr")),
-				err: errors.New(errIDNotInt),
+				err: errors.New(errNotMember),
 			},
 		},
 		"FailedGetRequest": {
 			args: args{
 				groupMember: &fake.MockClient{
 					MockGetMember: func(gid interface{}, user int, options ...gitlab.RequestOptionFunc) (*gitlab.GroupMember, *gitlab.Response, error) {
-						return nil, &gitlab.Response{Response: &http.Response{StatusCode: 400}}, errBoom
-					},
-				},
-				cr: groupMember(withExternalName(extName)),
-			},
-			want: want{
-				cr:     groupMember(withAnnotations(extNameAnnotation)),
-				result: managed.ExternalObservation{ResourceExists: false},
-				err:    errors.Wrap(errBoom, errGetFailed),
-			},
-		},
-		"ErrGet404": {
-			args: args{
-				groupMember: &fake.MockClient{
-					MockGetMember: func(gid interface{}, user int, options ...gitlab.RequestOptionFunc) (*gitlab.GroupMember, *gitlab.Response, error) {
-						return nil, &gitlab.Response{Response: &http.Response{StatusCode: 404}}, errBoom
+						return nil, &gitlab.Response{}, errBoom
 					},
 				},
 				cr: groupMember(withExternalName(extName)),
diff --git a/pkg/controller/projects/accesstokens/controller_test.go b/pkg/controller/projects/accesstokens/controller_test.go
index 324d6ab..e726d0f 100644
--- a/pkg/controller/projects/accesstokens/controller_test.go
+++ b/pkg/controller/projects/accesstokens/controller_test.go
@@ -19,7 +19,6 @@ package accesstokens
 import (
 	"context"
 	"fmt"
-	"net/http"
 	"strconv"
 	"testing"
 	"time"
@@ -149,7 +148,7 @@ func TestObserve(t *testing.T) {
 		"ErrGetAccessToken": {
 			args: args{
 				accessTokenClient: &fake.MockClient{
-					MockGetProjectAccessToken: func(pid interface{}, id int, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectAccessToken, *gitlab.Response, error) {
+					MockGetAccessTokens: func(pid interface{}, id int, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectAccessToken, *gitlab.Response, error) {
 						return nil, nil, errBoom
 					},
 				},
@@ -171,33 +170,10 @@ func TestObserve(t *testing.T) {
 				err:    errors.Wrap(errBoom, errAccessTokentNotFound),
 			},
 		},
-		"GetErr404": {
-			args: args{
-				accessTokenClient: &fake.MockClient{
-					MockGetProjectAccessToken: func(pid interface{}, id int, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectAccessToken, *gitlab.Response, error) {
-						return nil, &gitlab.Response{
-							Response: &http.Response{StatusCode: http.StatusNotFound},
-						}, errBoom
-					},
-				},
-				cr: accessToken(
-					withExternalName(sAccessTokenID),
-					withSpec(v1alpha1.AccessTokenParameters{ProjectID: &projectID}),
-				),
-			},
-			want: want{
-				cr: accessToken(
-					withExternalName(sAccessTokenID),
-					withSpec(v1alpha1.AccessTokenParameters{ProjectID: &projectID}),
-				),
-				result: managed.ExternalObservation{},
-				err:    nil,
-			},
-		},
 		"AccessTokenDoNotExist": {
 			args: args{
 				accessTokenClient: &fake.MockClient{
-					MockGetProjectAccessToken: func(pid interface{}, id int, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectAccessToken, *gitlab.Response, error) {
+					MockGetAccessTokens: func(pid interface{}, id int, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectAccessToken, *gitlab.Response, error) {
 						return nil, nil, errBoom
 					},
 				},
@@ -222,7 +198,7 @@ func TestObserve(t *testing.T) {
 		"ResourceLateInitializedFalse": {
 			args: args{
 				accessTokenClient: &fake.MockClient{
-					MockGetProjectAccessToken: func(pid interface{}, id int, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectAccessToken, *gitlab.Response, error) {
+					MockGetAccessTokens: func(pid interface{}, id int, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectAccessToken, *gitlab.Response, error) {
 						return &gitlab.ProjectAccessToken{}, &gitlab.Response{}, nil
 					},
 				},
@@ -255,7 +231,7 @@ func TestObserve(t *testing.T) {
 		"ResourceLateInitializedTrue": {
 			args: args{
 				accessTokenClient: &fake.MockClient{
-					MockGetProjectAccessToken: func(pid interface{}, id int, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectAccessToken, *gitlab.Response, error) {
+					MockGetAccessTokens: func(pid interface{}, id int, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectAccessToken, *gitlab.Response, error) {
 						return &gitlab.ProjectAccessToken{
 							ExpiresAt:   accessTokenObj.ExpiresAt,
 							AccessLevel: *gitlab.AccessLevel(accessTokenObj.AccessLevel),
@@ -289,7 +265,7 @@ func TestObserve(t *testing.T) {
 		"TokenUpToDate": {
 			args: args{
 				accessTokenClient: &fake.MockClient{
-					MockGetProjectAccessToken: func(pid interface{}, id int, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectAccessToken, *gitlab.Response, error) {
+					MockGetAccessTokens: func(pid interface{}, id int, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectAccessToken, *gitlab.Response, error) {
 						return &gitlab.ProjectAccessToken{}, &gitlab.Response{}, nil
 					},
 				},
@@ -373,7 +349,7 @@ func TestCreate(t *testing.T) {
 		"CreationFailedErr": {
 			args: args{
 				accessTokenClient: &fake.MockClient{
-					MockCreateProjectAccessToken: func(pid interface{}, opt *gitlab.CreateProjectAccessTokenOptions, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectAccessToken, *gitlab.Response, error) {
+					MockCreateAccessToken: func(pid interface{}, opt *gitlab.CreateProjectAccessTokenOptions, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectAccessToken, *gitlab.Response, error) {
 						return nil, nil, errBoom
 					},
 				},
@@ -398,7 +374,7 @@ func TestCreate(t *testing.T) {
 		"NoExternalName": {
 			args: args{
 				accessTokenClient: &fake.MockClient{
-					MockCreateProjectAccessToken: func(pid interface{}, opt *gitlab.CreateProjectAccessTokenOptions, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectAccessToken, *gitlab.Response, error) {
+					MockCreateAccessToken: func(pid interface{}, opt *gitlab.CreateProjectAccessTokenOptions, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectAccessToken, *gitlab.Response, error) {
 						return &gitlab.ProjectAccessToken{}, &gitlab.Response{}, errBoom
 					},
 				},
@@ -425,7 +401,7 @@ func TestCreate(t *testing.T) {
 					MockUpdate: test.NewMockUpdateFn(nil),
 				},
 				accessTokenClient: &fake.MockClient{
-					MockCreateProjectAccessToken: func(pid interface{}, opt *gitlab.CreateProjectAccessTokenOptions, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectAccessToken, *gitlab.Response, error) {
+					MockCreateAccessToken: func(pid interface{}, opt *gitlab.CreateProjectAccessTokenOptions, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectAccessToken, *gitlab.Response, error) {
 						return &accessTokenObj, &gitlab.Response{}, nil
 					},
 				},
@@ -528,7 +504,7 @@ func TestDelete(t *testing.T) {
 		"FailedDeletionExternalNameNotInt": {
 			args: args{
 				accessTokenClient: &fake.MockClient{
-					MockRevokeProjectAccessToken: func(pid interface{}, id int, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error) {
+					MockRevokeAccessToken: func(pid interface{}, id int, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error) {
 						return &gitlab.Response{}, nil
 					},
 				},
@@ -552,7 +528,7 @@ func TestDelete(t *testing.T) {
 		"FailedDeletion": {
 			args: args{
 				accessTokenClient: &fake.MockClient{
-					MockRevokeProjectAccessToken: func(pid interface{}, id int, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error) {
+					MockRevokeAccessToken: func(pid interface{}, id int, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error) {
 						return &gitlab.Response{}, errBoom
 					},
 				},
@@ -576,7 +552,7 @@ func TestDelete(t *testing.T) {
 		"SuccessfulDeletion": {
 			args: args{
 				accessTokenClient: &fake.MockClient{
-					MockRevokeProjectAccessToken: func(pid interface{}, id int, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error) {
+					MockRevokeAccessToken: func(pid interface{}, id int, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error) {
 						return &gitlab.Response{}, nil
 					},
 				},
diff --git a/pkg/controller/projects/deploykeys/controller_test.go b/pkg/controller/projects/deploykeys/controller_test.go
index 255b2b3..a7386f3 100644
--- a/pkg/controller/projects/deploykeys/controller_test.go
+++ b/pkg/controller/projects/deploykeys/controller_test.go
@@ -18,7 +18,6 @@ package deploykeys
 
 import (
 	"context"
-	"net/http"
 	"strconv"
 	"testing"
 	"time"
@@ -169,7 +168,7 @@ func TestObserve(t *testing.T) {
 				cr: buildDeployKey(withExternalName(testExternalName)),
 				deployKeyService: &fake.MockClient{
 					MockGetDeployKey: func(pid interface{}, deployKey int, options ...*gitlab.RequestOptionFunc) (*gitlab.ProjectDeployKey, *gitlab.Response, error) {
-						return nil, &gitlab.Response{Response: &http.Response{StatusCode: 400}}, errors.New(testGetKeyErrorMessage)
+						return nil, &gitlab.Response{}, errors.New(testGetKeyErrorMessage)
 					},
 				},
 			},
@@ -179,12 +178,12 @@ func TestObserve(t *testing.T) {
 				result: managed.ExternalObservation{},
 			},
 		},
-		"GetErr404": {
+		"GetKeyNotFound": {
 			args: args{
 				cr: buildDeployKey(withExternalName(testExternalName)),
 				deployKeyService: &fake.MockClient{
 					MockGetDeployKey: func(pid interface{}, deployKey int, options ...*gitlab.RequestOptionFunc) (*gitlab.ProjectDeployKey, *gitlab.Response, error) {
-						return nil, &gitlab.Response{Response: &http.Response{StatusCode: 404}}, errors.New("")
+						return nil, nil, nil
 					},
 				},
 			},
diff --git a/pkg/controller/projects/deploytokens/controller_test.go b/pkg/controller/projects/deploytokens/controller_test.go
index a1fd2bd..8cde2eb 100644
--- a/pkg/controller/projects/deploytokens/controller_test.go
+++ b/pkg/controller/projects/deploytokens/controller_test.go
@@ -19,7 +19,6 @@ package deploytokens
 import (
 	"context"
 	"fmt"
-	"net/http"
 	"strconv"
 	"testing"
 	"time"
@@ -161,11 +160,11 @@ func TestObserve(t *testing.T) {
 				err: errors.Wrap(errBoom, errGetFailed),
 			},
 		},
-		"ErrGet404": {
+		"DeployTokenNotFound": {
 			args: args{
 				deployToken: &fake.MockClient{
 					MockGetProjectDeployToken: func(pid interface{}, deployToken int, options ...gitlab.RequestOptionFunc) (*gitlab.DeployToken, *gitlab.Response, error) {
-						return nil, &gitlab.Response{Response: &http.Response{StatusCode: 404}}, errBoom
+						return nil, &gitlab.Response{}, nil
 					},
 				},
 				cr: deployToken(
@@ -191,7 +190,6 @@ func TestObserve(t *testing.T) {
 					ResourceUpToDate:        false,
 					ResourceLateInitialized: false,
 				},
-				err: nil,
 			},
 		},
 		"LateInitSuccess": {
diff --git a/pkg/controller/projects/hooks/controller_test.go b/pkg/controller/projects/hooks/controller_test.go
index fa78cac..5ce23de 100644
--- a/pkg/controller/projects/hooks/controller_test.go
+++ b/pkg/controller/projects/hooks/controller_test.go
@@ -19,7 +19,6 @@ package hooks
 import (
 	"context"
 	"fmt"
-	"net/http"
 	"testing"
 	"time"
 
@@ -201,27 +200,6 @@ func TestObserve(t *testing.T) {
 				},
 			},
 		},
-		"ErrGet404": {
-			args: args{
-				projecthook: &fake.MockClient{
-					MockGetHook: func(pid interface{}, hook int, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectHook, *gitlab.Response, error) {
-						return nil, &gitlab.Response{Response: &http.Response{StatusCode: 404}}, errBoom
-					},
-				},
-				cr: projecthook(
-					withProjectID(projectID),
-					withExternalName(projectHookID),
-				),
-			},
-			want: want{
-				cr: projecthook(
-					withProjectID(projectID),
-					withExternalName(projectHookID),
-				),
-				result: managed.ExternalObservation{},
-				err:    nil,
-			},
-		},
 	}
 
 	for name, tc := range cases {
diff --git a/pkg/controller/projects/members/controller_test.go b/pkg/controller/projects/members/controller_test.go
index 09c7a8e..1451f62 100644
--- a/pkg/controller/projects/members/controller_test.go
+++ b/pkg/controller/projects/members/controller_test.go
@@ -15,7 +15,6 @@ package members
 
 import (
 	"context"
-	"net/http"
 	"strconv"
 	"testing"
 	"time"
@@ -193,11 +192,11 @@ func TestObserve(t *testing.T) {
 				err: errors.New(errNotMember),
 			},
 		},
-		"ErrGet404": {
+		"FailedGetRequest": {
 			args: args{
 				projectMember: &fake.MockClient{
 					MockGetMember: func(gid interface{}, user int, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectMember, *gitlab.Response, error) {
-						return nil, &gitlab.Response{Response: &http.Response{StatusCode: 404}}, errBoom
+						return nil, &gitlab.Response{}, errBoom
 					},
 				},
 				cr: projectMember(withExternalName(extName)),
@@ -208,21 +207,6 @@ func TestObserve(t *testing.T) {
 				err:    nil,
 			},
 		},
-		"ErrGet": {
-			args: args{
-				projectMember: &fake.MockClient{
-					MockGetMember: func(gid interface{}, user int, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectMember, *gitlab.Response, error) {
-						return nil, nil, errBoom
-					},
-				},
-				cr: projectMember(withExternalName(extName)),
-			},
-			want: want{
-				cr:     projectMember(withAnnotations(extNameAnnotation)),
-				result: managed.ExternalObservation{ResourceExists: false},
-				err:    errors.Wrap(errBoom, errObserveFailed),
-			},
-		},
 		"SuccessfulAvailable": {
 			args: args{
 				projectMember: &fake.MockClient{
diff --git a/pkg/controller/projects/project_test.go b/pkg/controller/projects/project_test.go
index e5866fb..731fd45 100644
--- a/pkg/controller/projects/project_test.go
+++ b/pkg/controller/projects/project_test.go
@@ -18,7 +18,6 @@ package projects
 
 import (
 	"context"
-	"net/http"
 	"reflect"
 	"strconv"
 	"testing"
@@ -119,10 +118,6 @@ func withAnnotations(a map[string]string) projectModifier {
 	return func(p *v1alpha1.Project) { meta.AddAnnotations(p, a) }
 }
 
-func withMirrorUserIDNil() projectModifier {
-	return func(p *v1alpha1.Project) { p.Spec.ForProvider.MirrorUserID = nil }
-}
-
 func project(m ...projectModifier) *v1alpha1.Project {
 	cr := &v1alpha1.Project{}
 	for _, f := range m {
@@ -229,22 +224,7 @@ func TestObserve(t *testing.T) {
 			args: args{
 				project: &fake.MockClient{
 					MockGetProject: func(pid interface{}, opt *gitlab.GetProjectOptions, options ...gitlab.RequestOptionFunc) (*gitlab.Project, *gitlab.Response, error) {
-						return nil, &gitlab.Response{Response: &http.Response{StatusCode: 400}}, errBoom
-					},
-				},
-				cr: project(withExternalName(extName)),
-			},
-			want: want{
-				cr:     project(withExternalName(extName)),
-				result: managed.ExternalObservation{ResourceExists: false},
-				err:    errors.Wrap(errBoom, errGetFailed),
-			},
-		},
-		"ErrGet404": {
-			args: args{
-				project: &fake.MockClient{
-					MockGetProject: func(pid interface{}, opt *gitlab.GetProjectOptions, options ...gitlab.RequestOptionFunc) (*gitlab.Project, *gitlab.Response, error) {
-						return nil, &gitlab.Response{Response: &http.Response{StatusCode: 404}}, errBoom
+						return nil, &gitlab.Response{}, errBoom
 					},
 				},
 				cr: project(withExternalName(extName)),
@@ -312,37 +292,6 @@ func TestObserve(t *testing.T) {
 				},
 			},
 		},
-		"LateInitSuccessMirrorUserIdZero": {
-			args: args{
-				kube: &test.MockClient{
-					MockUpdate: test.NewMockUpdateFn(nil),
-				},
-				project: &fake.MockClient{
-					MockGetProject: func(pid interface{}, opt *gitlab.GetProjectOptions, options ...gitlab.RequestOptionFunc) (*gitlab.Project, *gitlab.Response, error) {
-						return &gitlab.Project{MirrorUserID: 0}, &gitlab.Response{}, nil
-					},
-				},
-				cr: project(
-					withClientDefaultValues(),
-					withMirrorUserIDNil(),
-					withExternalName(extName),
-				),
-			},
-			want: want{
-				cr: project(
-					withClientDefaultValues(),
-					withMirrorUserIDNil(),
-					withExternalName(extName),
-					withConditions(xpv1.Available()),
-				),
-				result: managed.ExternalObservation{
-					ResourceExists:          true,
-					ResourceUpToDate:        true,
-					ResourceLateInitialized: false,
-					ConnectionDetails:       managed.ConnectionDetails{"runnersToken": {}},
-				},
-			},
-		},
 	}
 
 	isProjectUpToDateCases := map[string]interface{}{
diff --git a/pkg/controller/projects/variables/controller_test.go b/pkg/controller/projects/variables/controller_test.go
index 29430dc..b474e55 100644
--- a/pkg/controller/projects/variables/controller_test.go
+++ b/pkg/controller/projects/variables/controller_test.go
@@ -18,7 +18,6 @@ package variables
 
 import (
 	"context"
-	"net/http"
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
@@ -240,7 +239,7 @@ func TestObserve(t *testing.T) {
 			args: args{
 				variable: &fake.MockClient{
 					MockGetVariable: func(pid interface{}, key string, opt *gitlab.GetProjectVariableOptions, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectVariable, *gitlab.Response, error) {
-						return &gitlab.ProjectVariable{}, &gitlab.Response{Response: &http.Response{StatusCode: 400}}, errBoom
+						return &gitlab.ProjectVariable{}, &gitlab.Response{}, errBoom
 					},
 				},
 				cr: variable(
@@ -257,27 +256,6 @@ func TestObserve(t *testing.T) {
 				err:    errors.Wrap(errBoom, errGetFailed),
 			},
 		},
-		"ErrGet404": {
-			args: args{
-				variable: &fake.MockClient{
-					MockGetVariable: func(pid interface{}, key string, opt *gitlab.GetProjectVariableOptions, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectVariable, *gitlab.Response, error) {
-						return &gitlab.ProjectVariable{}, &gitlab.Response{Response: &http.Response{StatusCode: 404}}, errBoom
-					},
-				},
-				cr: variable(
-					withDefaultValues(),
-					withValue("blah"),
-				),
-			},
-			want: want{
-				cr: variable(
-					withDefaultValues(),
-					withValue("blah"),
-				),
-				result: managed.ExternalObservation{},
-				err:    nil,
-			},
-		},
 		"ValueSecretRef": {
 			args: args{
 				kube: &test.MockClient{
@@ -348,7 +326,7 @@ func TestObserve(t *testing.T) {
 				},
 				variable: &fake.MockClient{
 					MockGetVariable: func(pid interface{}, key string, opt *gitlab.GetProjectVariableOptions, options ...gitlab.RequestOptionFunc) (*gitlab.ProjectVariable, *gitlab.Response, error) {
-						return &gitlab.ProjectVariable{}, &gitlab.Response{Response: &http.Response{StatusCode: 400}}, errors.New(errSecretKeyNotFound)
+						return &gitlab.ProjectVariable{}, &gitlab.Response{}, errors.New(errSecretKeyNotFound)
 					},
 				},
 				cr: variable(