diff --git a/components/automate-cli/cmd/chef-automate/verify.go b/components/automate-cli/cmd/chef-automate/verify.go index f605adb014c..d402747ed8f 100644 --- a/components/automate-cli/cmd/chef-automate/verify.go +++ b/components/automate-cli/cmd/chef-automate/verify.go @@ -688,8 +688,9 @@ func (v *verifyCmdFlow) printResponse(batchCheckResults []models.BatchCheckResul func buildReports(batchCheckResults []models.BatchCheckResult) []reporting.VerificationReport { var reports []reporting.VerificationReport - for _, batchCheckResult := range batchCheckResults { + updatedReportForSystemUser(&batchCheckResults) + for _, batchCheckResult := range batchCheckResults { for _, test := range batchCheckResult.Tests { var errorMsgs, resolutionMsgs []string @@ -751,6 +752,57 @@ func buildReports(batchCheckResults []models.BatchCheckResult) []reporting.Verif return reports } +func updatedReportForSystemUser(batchCheckResults *[]models.BatchCheckResult) { + isNFSCheckPresent := false + uids := []string{} + + for _, batchCheckResult := range *batchCheckResults { + // Check all IDs + for _, test := range batchCheckResult.Tests { + if test.Check == constants.NFS_BACKUP_CONFIG && !test.Skipped { + isNFSCheckPresent = true + } + if test.Check == constants.SYSTEM_USER { + if len(test.Checks) == 0 { + continue + } + if !arrayutils.Contains(uids, test.Id.UserID) { + uids = append(uids, test.Id.UserID) + } + + } + } + } + + for _, batchCheckResult := range *batchCheckResults { + if isNFSCheckPresent { + for i, test := range batchCheckResult.Tests { + if test.Check == constants.SYSTEM_USER { + var newCheck models.Checks + if len(uids) == 1 { + newCheck = models.Checks{ + Title: "User ID - validation", + Passed: true, + SuccessMsg: "hab uids are same across all nodes", + Skipped: false, + } + } else { + newCheck = models.Checks{ + Title: "User ID - validation", + Passed: false, + ErrorMsg: fmt.Sprintf("hab uid: %s. hab uid is not same across all nodes", test.Id.UserID), + ResolutionMsg: "hab uid should be same across all nodes/machines", + Skipped: false, + } + batchCheckResult.Tests[i].Passed = false + } + batchCheckResult.Tests[i].Checks = append(batchCheckResult.Tests[i].Checks, newCheck) + } + } + } + } +} + func createTables(numberOfAutomateNodes, numberOfChefServerNodes, numberOfPostgreSQLNodes, numberOfOpenSearchNodes int) map[string]*reporting.Table { bastionSummaryTableTitle := "Summary: Bastion - 1" automateSummaryTableTitle := fmt.Sprintf("Summary: Automate - %d", numberOfAutomateNodes) diff --git a/components/automate-cli/cmd/chef-automate/verify_test.go b/components/automate-cli/cmd/chef-automate/verify_test.go index e82c9917074..c86ba5037bd 100644 --- a/components/automate-cli/cmd/chef-automate/verify_test.go +++ b/components/automate-cli/cmd/chef-automate/verify_test.go @@ -20,11 +20,14 @@ import ( ) const ( - CONFIG_FILE = "/config_valid_config_parser.toml" - STATUS_API_RESPONSE = `{"status":"SUCCESS","result":{"status":"OK","services":[],"cli_version":"20230622174936","error":"error getting services from hab svc status"}}` - BATCH_CHECK_REQUEST = `{"status":"SUCCESS","result":{"passed":true,"node_result":[]}}` - AWS_CONFIG_FILE = "/valid_config.toml" - DARWIN = "darwin" + CONFIG_FILE = "/config_valid_config_parser.toml" + STATUS_API_RESPONSE = `{"status":"SUCCESS","result":{"status":"OK","services":[],"cli_version":"20230622174936","error":"error getting services from hab svc status"}}` + BATCH_CHECK_REQUEST = `{"status":"SUCCESS","result":{"passed":true,"node_result":[]}}` + BATCH_CHECK_REQUEST_HAB_ID_WITH_NFS = `{"status":"SUCCESS","result":{"passed":true,"node_result":[{"node_type":"opensearch","ip":"10.0.164.66","tests":[{"passed":true,"msg":"System User Check","check":"system-user","checks":[{"title":"User creation/validation check","passed":true,"success_msg":"User is created or found successfully","skipped":false}],"skipped":false,"id":{"user_id":"1001","group_id":"1001"}},{"passed": true,"msg": "NFS Backup Config Check","check": "nfs-backup-config"}]},{"node_type":"opensearch","ip":"10.0.133.98","tests":[{"passed":true,"msg":"System User Check","check":"system-user","checks":[{"title":"User creation/validation check","passed":true,"success_msg":"User is created or found successfully","skipped":false}],"skipped":false,"id":{"user_id":"1001","group_id":"1001"}}]},{"node_type":"opensearch","ip":"10.0.146.192","tests":[{"passed":true,"msg":"System User Check","check":"system-user","checks":[{"title":"User creation/validation check","passed":true,"success_msg":"User is created or found successfully","skipped":false}],"skipped":false,"id":{"user_id":"1001","group_id":"1001"}}]},{"node_type":"chef-infra-server","ip":"10.0.128.167","tests":[{"passed":true,"msg":"System User Check","check":"system-user","checks":[{"title":"User creation/validation check","passed":true,"success_msg":"User is created or found successfully","skipped":false}],"skipped":false,"id":{"user_id":"1001","group_id":"1001"}}]},{"node_type":"automate","ip":"10.0.130.38","tests":[{"passed":true,"msg":"System User Check","check":"system-user","checks":[{"title":"User creation/validation check","passed":true,"success_msg":"User is created or found successfully","skipped":false}],"skipped":false,"id":{"user_id":"1001","group_id":"1001"}}]},{"node_type":"postgresql","ip":"10.0.139.234","tests":[{"passed":true,"msg":"System User Check","check":"system-user","checks":[{"title":"User creation/validation check","passed":true,"success_msg":"User is created or found successfully","skipped":false}],"skipped":false,"id":{"user_id":"1001","group_id":"1001"}}]},{"node_type":"postgresql","ip":"10.0.168.59","tests":[{"passed":true,"msg":"System User Check","check":"system-user","checks":[{"title":"User creation/validation check","passed":true,"success_msg":"User is created or found successfully","skipped":false}],"skipped":false,"id":{"user_id":"1001","group_id":"1001"}}]},{"node_type":"postgresql","ip":"10.0.159.200","tests":[{"passed":true,"msg":"System User Check","check":"system-user","checks":[{"title":"User creation/validation check","passed":true,"success_msg":"User is created or found successfully","skipped":false}],"skipped":false,"id":{"user_id":"1001","group_id":"1001"}}]}]}}` + BATCH_CHECK_REQUEST_HAB_ID_WITH_NFS_FAILURE = `{"status":"SUCCESS","result":{"passed":true,"node_result":[{"node_type":"opensearch","ip":"10.0.164.66","tests":[{"passed":true,"msg":"System User Check","check":"system-user","checks":[{"title":"User creation/validation check","passed":true,"success_msg":"User is created or found successfully","skipped":false}],"skipped":false,"id":{"user_id":"1001","group_id":"1001"}},{"passed": true,"msg": "NFS Backup Config Check","check": "nfs-backup-config"}]},{"node_type":"opensearch","ip":"10.0.133.98","tests":[{"passed":true,"msg":"System User Check","check":"system-user","checks":[{"title":"User creation/validation check","passed":true,"success_msg":"User is created or found successfully","skipped":false}],"skipped":false,"id":{"user_id":"1001","group_id":"1001"}}]},{"node_type":"opensearch","ip":"10.0.146.192","tests":[{"passed":true,"msg":"System User Check","check":"system-user","checks":[{"title":"User creation/validation check","passed":true,"success_msg":"User is created or found successfully","skipped":false}],"skipped":false,"id":{"user_id":"1002","group_id":"1002"}}]},{"node_type":"chef-infra-server","ip":"10.0.128.167","tests":[{"passed":true,"msg":"System User Check","check":"system-user","checks":[{"title":"User creation/validation check","passed":true,"success_msg":"User is created or found successfully","skipped":false}],"skipped":false,"id":{"user_id":"1001","group_id":"1001"}}]},{"node_type":"automate","ip":"10.0.130.38","tests":[{"passed":true,"msg":"System User Check","check":"system-user","checks":[{"title":"User creation/validation check","passed":true,"success_msg":"User is created or found successfully","skipped":false}],"skipped":false,"id":{"user_id":"1001","group_id":"1001"}}]},{"node_type":"postgresql","ip":"10.0.139.234","tests":[{"passed":true,"msg":"System User Check","check":"system-user","checks":[{"title":"User creation/validation check","passed":true,"success_msg":"User is created or found successfully","skipped":false}],"skipped":false,"id":{"user_id":"1001","group_id":"1001"}}]},{"node_type":"postgresql","ip":"10.0.168.59","tests":[{"passed":true,"msg":"System User Check","check":"system-user","checks":[{"title":"User creation/validation check","passed":true,"success_msg":"User is created or found successfully","skipped":false}],"skipped":false,"id":{"user_id":"1001","group_id":"1001"}}]},{"node_type":"postgresql","ip":"10.0.159.200","tests":[{"passed":true,"msg":"System User Check","check":"system-user","checks":[{"title":"User creation/validation check","passed":true,"success_msg":"User is created or found successfully","skipped":false}],"skipped":false,"id":{"user_id":"1001","group_id":"1001"}}]}]}}` + BATCH_CHECK_REQUEST_HAB_ID_WITH_NFS_FAILURE_2 = `{"status":"SUCCESS","result":{"passed":true,"node_result":[{"node_type":"opensearch","ip":"10.0.164.66","tests":[{"passed":true,"msg":"System User Check","check":"system-user","checks":[],"skipped":false,"id":{"user_id":"1001","group_id":"1001"}},{"passed": true,"msg": "NFS Backup Config Check","check": "nfs-backup-config"}]},{"node_type":"opensearch","ip":"10.0.133.98","tests":[{"passed":true,"msg":"System User Check","check":"system-user","checks":[],"skipped":false,"id":{"user_id":"1001","group_id":"1001"}}]}]}}` + AWS_CONFIG_FILE = "/valid_config.toml" + DARWIN = "darwin" ) var AwsAutoTfvarsJsonStringEmpty = ` @@ -375,6 +378,165 @@ func TestRunVerifyCmd(t *testing.T) { configFile: "", wantErr: errors.New("Failed to populate HA common config"), }, + { + description: "bastion with aws automate-verify to check hab if with NFS - failure", + IsAws: true, + mockHttputils: &httputils.MockHTTPClient{ + MakeRequestFunc: func(requestMethod, url string, body interface{}) (*http.Response, []byte, error) { + if strings.Contains(url, "batch-check") { + return &http.Response{ + StatusCode: http.StatusOK, + Body: nil, + }, []byte(BATCH_CHECK_REQUEST_HAB_ID_WITH_NFS_FAILURE), nil + } + return &http.Response{ + StatusCode: http.StatusOK, + Body: nil, + }, []byte(STATUS_API_RESPONSE), nil + }, + }, + mockCreateSystemdService: &verifysystemdcreate.MockCreateSystemdService{ + CreateFun: func() error { + return nil + }, + }, + mockSystemdCreateUtils: &verifysystemdcreate.MockSystemdCreateUtils{ + GetBinaryPathFunc: func() (string, error) { + return "", nil + }, + }, + mockSSHUtil: &sshutils.MockSSHUtilsImpl{ + ExecuteConcurrentlyFunc: func(sshConfig sshutils.SSHConfig, cmd string, hostIPs []string) []sshutils.Result { + return []sshutils.Result{ + { + HostIP: "", + Error: nil, + Output: "", + }, + } + }, + CopyFileToRemoteConcurrentlyFunc: func(sshConfig sshutils.SSHConfig, srcFilePath string, destFileName string, destDir string, removeFile bool, hostIPs []string) []sshutils.Result { + return []sshutils.Result{ + { + HostIP: "", + Error: nil, + Output: "", + }, + } + }, + }, + configFile: CONFIG_AWS_TOML_PATH + AWS_CONFIG_FILE, + wantErr: nil, + ConvTfvarToJsonFunc: func(string) string { + return AwsAutoTfvarsJsonStringEmpty + }, + }, + { + description: "bastion with aws automate-verify to check hab if with NFS - failure_2", + IsAws: true, + mockHttputils: &httputils.MockHTTPClient{ + MakeRequestFunc: func(requestMethod, url string, body interface{}) (*http.Response, []byte, error) { + if strings.Contains(url, "batch-check") { + return &http.Response{ + StatusCode: http.StatusOK, + Body: nil, + }, []byte(BATCH_CHECK_REQUEST_HAB_ID_WITH_NFS_FAILURE_2), nil + } + return &http.Response{ + StatusCode: http.StatusOK, + Body: nil, + }, []byte(STATUS_API_RESPONSE), nil + }, + }, + mockCreateSystemdService: &verifysystemdcreate.MockCreateSystemdService{ + CreateFun: func() error { + return nil + }, + }, + mockSystemdCreateUtils: &verifysystemdcreate.MockSystemdCreateUtils{ + GetBinaryPathFunc: func() (string, error) { + return "", nil + }, + }, + mockSSHUtil: &sshutils.MockSSHUtilsImpl{ + ExecuteConcurrentlyFunc: func(sshConfig sshutils.SSHConfig, cmd string, hostIPs []string) []sshutils.Result { + return []sshutils.Result{ + { + HostIP: "", + Error: nil, + Output: "", + }, + } + }, + CopyFileToRemoteConcurrentlyFunc: func(sshConfig sshutils.SSHConfig, srcFilePath string, destFileName string, destDir string, removeFile bool, hostIPs []string) []sshutils.Result { + return []sshutils.Result{ + { + HostIP: "", + Error: nil, + Output: "", + }, + } + }, + }, + configFile: CONFIG_AWS_TOML_PATH + AWS_CONFIG_FILE, + wantErr: nil, + ConvTfvarToJsonFunc: func(string) string { + return AwsAutoTfvarsJsonStringEmpty + }, + }, + { + description: "bastion with aws automate-verify to check hab if with NFS - success", + IsAws: true, + mockHttputils: &httputils.MockHTTPClient{ + MakeRequestFunc: func(requestMethod, url string, body interface{}) (*http.Response, []byte, error) { + if strings.Contains(url, "batch-check") { + return &http.Response{ + StatusCode: http.StatusOK, + Body: nil, + }, []byte(BATCH_CHECK_REQUEST_HAB_ID_WITH_NFS), nil + } + return &http.Response{ + StatusCode: http.StatusOK, + Body: nil, + }, []byte(STATUS_API_RESPONSE), nil + }, + }, + mockCreateSystemdService: &verifysystemdcreate.MockCreateSystemdService{ + CreateFun: func() error { + return nil + }, + }, + mockSystemdCreateUtils: &verifysystemdcreate.MockSystemdCreateUtils{ + GetBinaryPathFunc: func() (string, error) { + return "", nil + }, + }, + mockSSHUtil: &sshutils.MockSSHUtilsImpl{ + ExecuteConcurrentlyFunc: func(sshConfig sshutils.SSHConfig, cmd string, hostIPs []string) []sshutils.Result { + return []sshutils.Result{ + { + HostIP: "", + Error: nil, + Output: "", + }, + } + }, + CopyFileToRemoteConcurrentlyFunc: func(sshConfig sshutils.SSHConfig, srcFilePath string, destFileName string, destDir string, removeFile bool, hostIPs []string) []sshutils.Result { + return []sshutils.Result{ + { + HostIP: "", + Error: nil, + Output: "", + }, + } + }, + }, + configFile: CONFIG_AWS_TOML_PATH + AWS_CONFIG_FILE, + wantErr: nil, + ConvTfvarToJsonFunc: func(string) string { + return AwsAutoTfvarsJsonStringEmpty + }, + }, } for _, tt := range tests { diff --git a/components/automate-cli/pkg/verifyserver/models/batchcheck.go b/components/automate-cli/pkg/verifyserver/models/batchcheck.go index 862d63f635e..e05908a2b89 100644 --- a/components/automate-cli/pkg/verifyserver/models/batchcheck.go +++ b/components/automate-cli/pkg/verifyserver/models/batchcheck.go @@ -521,13 +521,14 @@ type CheckTriggerResponse struct { CheckType string `json:"check_type"` } type ApiResult struct { - Passed bool `json:"passed"` - Message string `json:"msg"` - Check string `json:"check"` - Checks []Checks `json:"checks"` - Error *fiber.Error `json:"error,omitempty"` - Skipped bool `json:"skipped"` - SkipMessage string `json:"skip_message,omitempty"` + Passed bool `json:"passed"` + Message string `json:"msg"` + Check string `json:"check"` + Checks []Checks `json:"checks"` + Error *fiber.Error `json:"error,omitempty"` + Skipped bool `json:"skipped"` + SkipMessage string `json:"skip_message,omitempty"` + Id *SystemUserID `json:"id,omitempty"` } type Checks struct { diff --git a/components/automate-cli/pkg/verifyserver/models/systemuser.go b/components/automate-cli/pkg/verifyserver/models/systemuser.go index 9b0178f3388..1690daaf199 100644 --- a/components/automate-cli/pkg/verifyserver/models/systemuser.go +++ b/components/automate-cli/pkg/verifyserver/models/systemuser.go @@ -1,6 +1,12 @@ package models type SystemUserResponse struct { - Passed bool `json:"passed"` - Checks []*Checks `json:"checks"` + Passed bool `json:"passed"` + Checks []*Checks `json:"checks"` + Id *SystemUserID `json:"id,omitempty"` +} + +type SystemUserID struct { + UserID string `json:"user_id,omitempty"` + GroupID string `json:"group_id,omitempty"` } diff --git a/components/automate-cli/pkg/verifyserver/services/systemuserservice/systemuserservice.go b/components/automate-cli/pkg/verifyserver/services/systemuserservice/systemuserservice.go index e6693f3ad22..4be64d025c4 100644 --- a/components/automate-cli/pkg/verifyserver/services/systemuserservice/systemuserservice.go +++ b/components/automate-cli/pkg/verifyserver/services/systemuserservice/systemuserservice.go @@ -19,7 +19,7 @@ type SystemUserServiceImp struct { } func NewSystemUserService(log logger.Logger, exec executil.ExecCmdService, user userutils.UserUtil) *SystemUserServiceImp { - return &SystemUserServiceImp { + return &SystemUserServiceImp{ exec: exec, user: user, Log: log, @@ -36,6 +36,16 @@ func (su *SystemUserServiceImp) GetSystemUserServiceDetails() *models.SystemUser serviceResponse.Passed = false } + // Gets UID for created/existing hab user + uid, err := su.getUserAndGroupID() + if err != nil { + serviceResponse.Passed = false + su.Log.Error("User ID for hab not found") + } + su.Log.Info("User ID for hab found") + + serviceResponse.Id = uid + habGroupResponse := su.ValidateHabGroup() if !habGroupResponse.Passed { serviceResponse.Passed = false @@ -81,6 +91,28 @@ func (su *SystemUserServiceImp) ValidateOrCreateHabUser() (*models.Checks, bool) return successResponse(constants.SYSTEM_USER_HAB_VALIDATION_SUCCESS_TITLE, constants.SYSTEM_USER_HAB_SUCCESS_MSG), false } +func (su *SystemUserServiceImp) getUserAndGroupID() (*models.SystemUserID, error) { + + systemUserDetails := &models.SystemUserID{} + + // Check for available ID in all machine (?) + ids, err := su.user.Lookup(constants.USER_NAME) + if err != nil { + su.Log.Error("User not found:", err) + return systemUserDetails, err + } + + if ids != nil { + systemUserDetails.UserID = ids.Uid + systemUserDetails.GroupID = ids.Gid + su.Log.Infof("UID: %s\nGID: %s\n", systemUserDetails.UserID, systemUserDetails.GroupID) + } else { + su.Log.Info("UID not found.") + } + su.Log.Info("Created 'hab' user and group ") + return systemUserDetails, nil +} + func (su *SystemUserServiceImp) ValidateHabGroup() *models.Checks { isHabGroupPresent := su.isHabGroupPresent(constants.GROUP_NAME) @@ -146,7 +178,7 @@ func (su *SystemUserServiceImp) checkUserPrimaryGroup(username, groupname string su.Log.Debug("User's primary group is not hab") return false } - su.Log.Debug("User's primary group is hab with gid '"+ sysUser.Gid+"'") + su.Log.Debug("User's primary group is hab with gid '" + sysUser.Gid + "'") return true } diff --git a/components/automate-cli/pkg/verifyserver/services/systemuserservice/systemuserservice_test.go b/components/automate-cli/pkg/verifyserver/services/systemuserservice/systemuserservice_test.go index 827a40a8633..338a2d348fc 100644 --- a/components/automate-cli/pkg/verifyserver/services/systemuserservice/systemuserservice_test.go +++ b/components/automate-cli/pkg/verifyserver/services/systemuserservice/systemuserservice_test.go @@ -7,9 +7,9 @@ import ( "github.com/chef/automate/components/automate-cli/pkg/verifyserver/constants" "github.com/chef/automate/components/automate-cli/pkg/verifyserver/models" - "github.com/chef/automate/lib/userutils" "github.com/chef/automate/lib/executil" "github.com/chef/automate/lib/logger" + "github.com/chef/automate/lib/userutils" "github.com/stretchr/testify/assert" ) @@ -53,6 +53,36 @@ func TestValidateHabUserSuccess(t *testing.T) { }, service) } +func TestGetUserAndGroupID(t *testing.T) { + mockUser := &userutils.UserUtilMock{ + LookupFunc: func(name string) (*user.User, error) { + return &user.User{ + Uid: "1002", + Gid: "1002", + }, nil + }, + } + + mockExec := &executil.ExecCmdServiceMock{ + CommandFunc: func(name string, args []string) ([]byte, error) { + return []byte{}, nil + }, + } + log, err := logger.NewLogger("text", "debug") + if err != nil { + assert.Error(t, err) + } + s := &SystemUserServiceImp{ + Log: log, + user: mockUser, + exec: mockExec, + } + service, _ := s.getUserAndGroupID() + + assert.Equal(t, service.UserID, "1002") + assert.Equal(t, service.GroupID, "1002") +} + func TestValidateHabUserFailure(t *testing.T) { mockUser := &userutils.UserUtilMock{ LookupFunc: func(name string) (*user.User, error) { @@ -80,7 +110,7 @@ func TestValidateHabUserFailure(t *testing.T) { Title: constants.SYSTEM_USER_HAB_VALIDATION_FAILURE_TITLE, Passed: false, SuccessMsg: "", - ErrorMsg: constants.SYSTEM_USER_HAB_ERROR_MSG , + ErrorMsg: constants.SYSTEM_USER_HAB_ERROR_MSG, ResolutionMsg: constants.SYSTEM_USER_HAB_RESOLUTION_MSG, }, service) } @@ -370,6 +400,10 @@ func TestGetSystemUserServiceDetailsSuccess(t *testing.T) { ResolutionMsg: "", }, }, + Id: &models.SystemUserID{ + UserID: "1001", + GroupID: "1001", + }, }, }, } @@ -421,7 +455,7 @@ func TestGetSystemUserServiceDetailsFailed(t *testing.T) { Title: constants.SYSTEM_USER_HAB_VALIDATION_FAILURE_TITLE, Passed: false, SuccessMsg: "", - ErrorMsg: constants.SYSTEM_USER_HAB_ERROR_MSG , + ErrorMsg: constants.SYSTEM_USER_HAB_ERROR_MSG, ResolutionMsg: constants.SYSTEM_USER_HAB_RESOLUTION_MSG, }, { @@ -439,6 +473,7 @@ func TestGetSystemUserServiceDetailsFailed(t *testing.T) { ResolutionMsg: constants.SYSTEM_USERANDGROUP_MAPPING_RESOLUTION_MSG, }, }, + Id: &models.SystemUserID{UserID: "", GroupID: ""}, }, }, } diff --git a/lib/arrayutils/remove_duplicate.go b/lib/arrayutils/remove_duplicate.go index 291dcadde8c..49f18334052 100644 --- a/lib/arrayutils/remove_duplicate.go +++ b/lib/arrayutils/remove_duplicate.go @@ -3,14 +3,14 @@ package arrayutils func RemoveStringDuplicates(inputArray []string) []string { arrayWithoutDuplicate := []string{} for _, item := range inputArray { - if !contains(arrayWithoutDuplicate, item) { + if !Contains(arrayWithoutDuplicate, item) { arrayWithoutDuplicate = append(arrayWithoutDuplicate, item) } } return arrayWithoutDuplicate } -func contains(arrayWithoutDuplicate []string, item string) bool { +func Contains(arrayWithoutDuplicate []string, item string) bool { index := -1 for i := range arrayWithoutDuplicate { if item == arrayWithoutDuplicate[i] {