Skip to content

Commit

Permalink
Merge pull request rancher#16 from crobby/migrationreview5
Browse files Browse the repository at this point in the history
More post review updates
  • Loading branch information
nflynt authored Aug 10, 2023
2 parents 4c98764 + b99cab4 commit 275f42b
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 29 deletions.
71 changes: 51 additions & 20 deletions cleanup/ad-guid-unmigration.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,14 @@ show_usage() {
if [ -n "$1" ]; then
echo -e "${RED}👉 $1${CLEAR}\n";
fi
echo -e "Usage: $0 [AGENT_IMAGE] [FLAGS]"
echo "AGENT_IMAGE is a required argument"
echo ""
echo "Flags:"
echo -e "\t-dry-run Display the resources that would will be updated without making changes"
echo -e "\t-delete-missing Permanently remove user objects whose GUID cannot be found in Active Directory"
echo "Usage: $0 AGENT_IMAGE [OPTIONS]"
echo ""
echo "Options:"
echo -e "\t-h, --help Display this help message"
echo -e "\t-n, --dry-run Display the resources that would be updated without making changes"
echo -e "\t-d, --delete-missing Permanently remove user objects whose GUID cannot be found in Active Directory"
}

# Function to display text in a banner format
display_banner() {
local text="$1"
local border_char="="
Expand All @@ -58,35 +57,67 @@ display_banner() {
echo "$border"
}

OPTS=$(getopt -o hnd -l help,dry-run,delete-missing -- "$@")
if [ $? != 0 ]; then
show_usage "Invalid option"
exit 1
fi

eval set -- "$OPTS"

dry_run=false
delete_missing=false

while true; do
case "$1" in
-h | --help)
show_usage
exit 0
;;
-n | --dry-run)
dry_run=true
shift
;;
-d | --delete-missing)
delete_missing=true
shift
;;
--)
shift
break
;;
*)
show_usage "Invalid option"
exit 1
;;
esac
done

shift "$((OPTIND - 1))"
# Ensure AGENT_IMAGE is provided
if [ $# -lt 1 ]; then
show_usage "AGENT_IMAGE is a required argument"
exit 1
fi

display_banner "${banner_text}"
read -p "Do you want to continue? (y/n): " choice
if [[ ! $choice =~ ^[Yy]$ ]]; then
echo "Exiting..."
exit 0
fi

if [ $# -lt 1 ]
then
show_usage "AGENT_IMAGE is a required argument"
exit 1
fi

if [[ $1 == "-h" ||$1 == "--help" ]]
then
show_usage
exit 0
fi

# Pull the yaml and replace the agent_image holder with the passed in image
# yaml=$(curl --insecure -sfL $yaml_url | sed -e 's=agent_image='"$agent_image"'=')
# Except it isn't pushed anywhere useful yet, so instead read the local file
yaml=$(cat ad-guid-unmigration.yaml | sed -e 's=agent_image='"$agent_image"'=')

if [ "$2" = "-dry-run" ]
if [ "$dry_run" = true ]
then
# Uncomment the env var for dry-run mode
yaml=$(sed -e 's/#dryrun // ' <<< "$yaml")
elif [ "$2" = "-delete-missing" ]
elif [ "$delete_missing" = true ]
then
# Instead uncomment the env var for missing user cleanup
yaml=$(sed -e 's/#deletemissing // ' <<< "$yaml")
Expand Down
6 changes: 6 additions & 0 deletions cleanup/ad-guid-unmigration.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ kind: ServiceAccount
metadata:
name: cattle-cleanup-sa
namespace: default
labels:
rancher-cleanup: "true"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: cattle-cleanup-binding
namespace: default
labels:
rancher-cleanup: "true"
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
Expand Down Expand Up @@ -61,6 +65,8 @@ kind: ClusterRole
metadata:
name: cattle-cleanup-role
namespace: default
labels:
rancher-cleanup: "true"
rules:
- apiGroups:
- '*'
Expand Down
53 changes: 44 additions & 9 deletions pkg/agent/clean/active_directory.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ const (
migrationPreviousName = "ad-guid-previous-name"
AttributeObjectClass = "objectClass"
AttributeObjectGUID = "objectGUID"
migrateStatusSkipped = "skippedUsers"
migrateStatusMissing = "missingUsers"
migrationStatusPercentage = "percentDone"
)

var validHexPattern = regexp.MustCompile("^[0-9a-f]+$")
Expand Down Expand Up @@ -84,14 +87,14 @@ func (e LdapErrorNotFound) Error() string {
return "ldap query returned no results"
}

// LdapFoundDuplicateGuid indicates either a configuration error or
// LdapFoundDuplicateGUID indicates either a configuration error or
// a corruption on the Active Directory side. In theory it should never
// be possible when talking to a real Active Directory server, but just
// in case we detect and handle it anyway.
type LdapFoundDuplicateGuid struct{}
type LdapFoundDuplicateGUID struct{}

// Error provides a string representation of an LdapErrorNotFound
func (e LdapFoundDuplicateGuid) Error() string {
func (e LdapFoundDuplicateGUID) Error() string {
return "ldap query returned multiple users for the same GUID"
}

Expand Down Expand Up @@ -235,7 +238,7 @@ func findDistinguishedName(guid string, lConn *ldapv3.Conn, adConfig *v3.ActiveD
if len(result.Entries) < 1 {
return "", LdapErrorNotFound{}
} else if len(result.Entries) > 1 {
return "", LdapFoundDuplicateGuid{}
return "", LdapFoundDuplicateGUID{}
}

entry := result.Entries[0]
Expand All @@ -249,7 +252,7 @@ func findDistinguishedNameWithRetries(guid string, lConn *ldapv3.Conn, adConfig

for retry := 0; retry < maxRetries; retry++ {
distinguishedName, err := findDistinguishedName(guid, lConn, adConfig)
if err == nil || errors.Is(err, LdapErrorNotFound{}) || errors.Is(err, LdapFoundDuplicateGuid{}) {
if err == nil || errors.Is(err, LdapErrorNotFound{}) || errors.Is(err, LdapFoundDuplicateGUID{}) {
return distinguishedName, err
}
logrus.Warnf("[%v] LDAP connection failed: '%v', retrying in %v...", migrateAdUserOperation, err, retryDelay.Seconds())
Expand Down Expand Up @@ -365,20 +368,23 @@ func UnmigrateAdGUIDUsers(clientConfig *restclient.Config, dryRun bool, deleteMi

for _, user := range skippedUsers {
logrus.Errorf("[%v] unable to migrate user '%v' due to a connection failure; this user will be skipped", migrateAdUserOperation, user.originalUser.Name)
updateUnmigratedUsers(user.originalUser.Name, migrateStatusSkipped, sc)
}
for _, missingUser := range missingUsers {
if deleteMissingUsers && !dryRun {
logrus.Infof("[%v] user '%v' with GUID '%v' does not seem to exist in Active Directory. deleteMissingUsers is true, proceeding to delete this user permanently", migrateAdUserOperation, missingUser.originalUser.Name, missingUser.guid)
updateUnmigratedUsers(missingUser.originalUser.Name, migrateStatusMissing, sc)
err = sc.Management.Users("").Delete(missingUser.originalUser.Name, &metav1.DeleteOptions{})
if err != nil {
logrus.Errorf("[%v] failed to delete missing user '%v' with: %v", migrateAdUserOperation, missingUser.originalUser.Name, err)
}
} else {
logrus.Errorf("[%v] User '%v' with GUID '%v' does not seem to exist in Active Directory. this user will be skipped", migrateAdUserOperation, missingUser.originalUser.Name, missingUser.guid)
updateUnmigratedUsers(missingUser.originalUser.Name, migrateStatusMissing, sc)
}
}

for _, userToMigrate := range usersToMigrate {
for i, userToMigrate := range usersToMigrate {
// If any of the binding replacements fail, then the resulting rancher state for this user is inconsistent
// and we should NOT attempt to modify the user or delete any of its duplicates. This situation is unusual
// and must be investigated by the local admin.
Expand Down Expand Up @@ -407,6 +413,12 @@ func UnmigrateAdGUIDUsers(clientConfig *restclient.Config, dryRun bool, deleteMi
updateModifiedUser(userToMigrate, sc)
}
}
percentDone := float64(i+1) / float64(len(usersToMigrate)) * 100
progress := fmt.Sprintf("%.0f%%", percentDone)
err = updateMigrationStatus(sc, migrationStatusPercentage, progress)
if err != nil {
logrus.Errorf("unable to update migration status: %v", err)
}
}

err = updateMigrationStatus(sc, activedirectory.StatusMigrationField, activedirectory.StatusMigrationFinished)
Expand Down Expand Up @@ -528,7 +540,7 @@ func identifyMigrationWorkUnits(users *v3.UserList, lConn *ldapv3.Conn, adConfig
logrus.Warnf("[%v] LDAP connection has permanently failed! will continue to migrate previously identified users", identifyAdUserOperation)
skippedUsers = append(skippedUsers, skippedUserWorkUnit{guid: guid, originalUser: user.DeepCopy()})
ldapPermanentlyFailed = true
} else if errors.Is(err, LdapFoundDuplicateGuid{}) {
} else if errors.Is(err, LdapFoundDuplicateGUID{}) {
logrus.Errorf("[%v] LDAP returned multiple users with GUID '%v'. this should not be possible, and may indicate a configuration error! this user will be skipped", identifyAdUserOperation, guid)
skippedUsers = append(skippedUsers, skippedUserWorkUnit{guid: guid, originalUser: user.DeepCopy()})
} else if errors.Is(err, LdapErrorNotFound{}) {
Expand Down Expand Up @@ -1037,8 +1049,10 @@ func updateMigrationStatus(sc *config.ScaledContext, status string, value string
},
}
}

cm.Data = map[string]string{status: value}
if cm.Data == nil {
cm.Data = map[string]string{}
}
cm.Data[status] = value

if _, err := sc.Core.ConfigMaps(activedirectory.StatusConfigMapNamespace).Update(cm); err != nil {
// If the ConfigMap does not exist, create it
Expand All @@ -1052,3 +1066,24 @@ func updateMigrationStatus(sc *config.ScaledContext, status string, value string

return nil
}

// updateUnmigratedUsers will add a user to the list for the specified migration status in the migration status configmap
func updateUnmigratedUsers(user string, status string, sc *config.ScaledContext) {
cm, err := sc.Core.ConfigMaps(activedirectory.StatusConfigMapNamespace).Get(activedirectory.StatusConfigMapName, metav1.GetOptions{})
if err != nil {
logrus.Errorf("[%v] unable to fetch configmap to update %v users: %v", migrateAdUserOperation, status, err)
}
currentList := cm.Data[status]
if currentList == "" {
currentList = currentList + user
} else {
currentList = currentList + "," + user
}
cm.Data[status] = currentList

if _, err := sc.Core.ConfigMaps(activedirectory.StatusConfigMapNamespace).Update(cm); err != nil {
if err != nil {
logrus.Errorf("[%v] unable to update migration status configmap: %v", migrateAdUserOperation, err)
}
}
}

0 comments on commit 275f42b

Please sign in to comment.