diff --git a/pkg/iris/scan.go b/pkg/iris/scan.go index b46ee5e..aa9b1f5 100644 --- a/pkg/iris/scan.go +++ b/pkg/iris/scan.go @@ -144,7 +144,18 @@ func (s *scanner) ScanNamespaces(kubernetesConfig models.KubernetesConfig, kuber func (s *scanner) ScanWorkloads(kubernetesAPI *kubernetes.API, kubernetesConfig models.KubernetesConfig) error { mapper := workloadMap.NewMapper(kubernetesAPI, kubernetesConfig.Cluster, s.workspaceId, s.runId) - discoveredWorkloads, err := s.ProcessWorkloads(mapper, kubernetesConfig.Cluster) + + nodes, err := kubernetesAPI.Nodes() + if err != nil { + return s.LogAndShareError("Scan failed while retrieving k8s cluster nodes. RunId: [%s], with reason: '%v'", ERROR, err, kubernetesConfig.ID) + } + + clusterInfo, err := mapper.MapCluster(kubernetesConfig.Cluster, nodes) + if err != nil { + return s.LogAndShareError("Scan failed while aggregating cluster information. RunId: [%s], with reason: '%v'", ERROR, err, kubernetesConfig.ID) + } + + discoveredWorkloads, err := s.ProcessWorkloads(mapper, clusterInfo) if err != nil { return s.LogAndShareError("Scan failed while retrieving k8s workload. RunId: [%s], with reason: '%v'", ERROR, err, kubernetesConfig.ID) } @@ -193,8 +204,8 @@ func (s *scanner) ProcessNamespace(k8sApi *kubernetes.API, mapper namespaceMap.M return ecstData, nil } -func (s *scanner) ProcessWorkloads(mapper workloadMap.MapperWorkload, clusterName string) ([]workload.Workload, error) { - return mapper.MapWorkloads(clusterName) +func (s *scanner) ProcessWorkloads(mapper workloadMap.WorkloadMapper, clusterInfo workload.Cluster) ([]workload.Data, error) { + return mapper.MapWorkloads(clusterInfo) } func (s *scanner) LogAndShareError(message string, loglevel string, err error, id string) error { diff --git a/pkg/iris/workloads/models/discoveryItemEcst.go b/pkg/iris/workloads/models/discoveryItemEcst.go index 1a929fd..c3bbc73 100644 --- a/pkg/iris/workloads/models/discoveryItemEcst.go +++ b/pkg/iris/workloads/models/discoveryItemEcst.go @@ -1,32 +1,29 @@ package models type Data struct { - Workload Workload `json:"workload"` -} - -type Service struct { - Name string `json:"name"` + Workload Workload `json:"workload"` + NamespaceName string `json:"namespaceName"` + ServiceName string `json:"serviceName"` + Cluster Cluster `json:"cluster"` + Timestamp string `json:"timestamp"` } type Workload struct { - ClusterName string `json:"clusterName"` - WorkloadType string `json:"workloadType"` - WorkloadName string `json:"workloadName"` - Containers Containers `json:"containers"` - ServiceName string `json:"serviceName"` - Labels interface{} `json:"labels"` - Timestamp string `json:"timestamp"` - WorkloadProperties Properties `json:"WorkloadProperties"` + Name string `json:"name"` + WorkloadType string `json:"type"` + Labels map[string]string `json:"labels"` + WorkloadProperties WorkloadProperties `json:"workloadProperties"` } -type Properties struct { - Schedule string `json:"schedule"` - Replicas string `json:"replicas"` - UpdateStrategy string `json:"updateStrategy"` +type WorkloadProperties struct { + Schedule string `json:"schedule"` + Replicas string `json:"replicas"` + UpdateStrategy string `json:"updateStrategy"` + Containers Containers `json:"containers"` } type Containers struct { - Name string `json:"containerName"` + Name string `json:"name"` Image string `json:"image"` Port interface{} `json:"port"` K8sLimits K8sResources `json:"k8sLimits"` @@ -37,3 +34,10 @@ type K8sResources struct { Cpu string `json:"cpu"` Memory string `json:"memory"` } + +type Cluster struct { + Name string `json:"name"` + OsImage string `json:"os"` + K8sVersion string `json:"k8sVersion"` + NoOfNodes int `json:"noOfNodes"` +} diff --git a/pkg/iris/workloads/models/eventsEcst.go b/pkg/iris/workloads/models/eventsEcst.go index 4074379..35db4bf 100644 --- a/pkg/iris/workloads/models/eventsEcst.go +++ b/pkg/iris/workloads/models/eventsEcst.go @@ -128,9 +128,9 @@ func CreateEcstDiscoveryEvent(eventType string, changeAction string, data Data, body := models.DiscoveryBody{ State: models.State{ - Name: fmt.Sprintf("%s/%s", data.Workload.WorkloadType, data.Workload.WorkloadName), + Name: fmt.Sprintf("%s/%s", data.Workload.WorkloadType, data.Workload.Name), SourceType: "kubernetes", - SourceInstance: fmt.Sprintf("cluster/%s", data.Workload.ClusterName), + SourceInstance: fmt.Sprintf("cluster/%s", data.Cluster.Name), Time: time, Data: data, }, @@ -147,7 +147,7 @@ func CreateEcstDiscoveryEvent(eventType string, changeAction string, data Data, func GenerateId(workspaceId string, configId string, data Data) string { scope := fmt.Sprintf(models.EventScopeFormat, workspaceId, configId) // workspace/{workspaceId}/configuration/{configurationId}/discoveryItem/service/kubernetes/workload/{clusterName}/{workloadType}/{workloadName} - idString := fmt.Sprintf("%s/%s/%s/%s/%s", scope, models.EventClassWorkload, data.Workload.ClusterName, data.Workload.WorkloadType, data.Workload.WorkloadName) + idString := fmt.Sprintf("%s/%s/%s/%s/%s", scope, models.EventClassWorkload, data.Cluster.Name, data.Workload.WorkloadType, data.Workload.Name) sum := sha256.Sum256([]byte(idString)) id := hex.EncodeToString(sum[:]) return id diff --git a/pkg/iris/workloads/services/events/eventProducer.go b/pkg/iris/workloads/services/events/eventProducer.go index fcfb419..ac74e58 100644 --- a/pkg/iris/workloads/services/events/eventProducer.go +++ b/pkg/iris/workloads/services/events/eventProducer.go @@ -11,7 +11,7 @@ import ( ) type WorkloadEventProducer interface { - ProcessWorkloads(data []workload.Workload, oldData []models.DiscoveryEvent, configId string) error + ProcessWorkloads(data []workload.Data, oldData []models.DiscoveryEvent, configId string) error PostStatus(status []byte) error FilterForChangedItems(newData map[string]workload.Data, oldData map[string]models.DiscoveryEvent, configId string) ([]models.DiscoveryEvent, []models.DiscoveryEvent, map[string]models.DiscoveryEvent, error) } @@ -30,7 +30,7 @@ func NewEventWorkloadProducer(irisApi common.IrisApi, runId string, workspaceId } } -func (p *workloadEventProducer) ProcessWorkloads(data []workload.Workload, oldData []models.DiscoveryEvent, configId string) error { +func (p *workloadEventProducer) ProcessWorkloads(data []workload.Data, oldData []models.DiscoveryEvent, configId string) error { created, updated, deleted, err := p.CreateECSTWorkloadEvents(data, oldData, configId) if err != nil { return err @@ -52,7 +52,7 @@ func (p *workloadEventProducer) PostStatus(status []byte) error { return p.irisApi.PostStatus(status) } -func (p *workloadEventProducer) CreateECSTWorkloadEvents(data []workload.Workload, oldData []models.DiscoveryEvent, configId string) ([]models.DiscoveryEvent, []models.DiscoveryEvent, []models.DiscoveryEvent, error) { +func (p *workloadEventProducer) CreateECSTWorkloadEvents(data []workload.Data, oldData []models.DiscoveryEvent, configId string) ([]models.DiscoveryEvent, []models.DiscoveryEvent, []models.DiscoveryEvent, error) { resultMap := p.createItemMap(data, configId) oldResultMap := p.createOldItemMap(oldData) @@ -70,13 +70,12 @@ func (p *workloadEventProducer) CreateECSTWorkloadEvents(data []workload.Workloa } -func (p *workloadEventProducer) createItemMap(workflows []workload.Workload, configId string) map[string]workload.Data { +func (p *workloadEventProducer) createItemMap(workloads []workload.Data, configId string) map[string]workload.Data { resultMap := map[string]workload.Data{} - for _, item := range workflows { - data := workload.Data{Workload: item} + for _, item := range workloads { // Build unique string hash for discoveryItem - id := workload.GenerateId(p.workspaceId, configId, data) - resultMap[id] = data + id := workload.GenerateId(p.workspaceId, configId, item) + resultMap[id] = item } return resultMap } diff --git a/pkg/iris/workloads/services/events/eventProducer_test.go b/pkg/iris/workloads/services/events/eventProducer_test.go index 15572c0..b0b6675 100644 --- a/pkg/iris/workloads/services/events/eventProducer_test.go +++ b/pkg/iris/workloads/services/events/eventProducer_test.go @@ -17,23 +17,29 @@ func Test_eventProducer_filter_created(t *testing.T) { newWorkload := map[string]workload.Data{ "workload": { Workload: workload.Workload{ - ClusterName: "testCluster1", - WorkloadName: "testWorkload1", + Name: "testWorkload1", WorkloadType: "deployment", - Containers: workload.Containers{ - Name: "testContainer1", - Image: "testImage1", - Port: "8080", + Labels: map[string]string{ + "k8s-app": "vsm-kubernetes", }, - ServiceName: "serviceName1", - Labels: "k8s-app", - Timestamp: "", - WorkloadProperties: workload.Properties{ + WorkloadProperties: workload.WorkloadProperties{ Schedule: "testSchedule", Replicas: "1", UpdateStrategy: "rollback", + Containers: workload.Containers{ + Name: "testContainer1", + Image: "testImage1", + Port: "8080", + }, }, }, + NamespaceName: "namespace-name", + ServiceName: "serviceName1", + Cluster: workload.Cluster{ + Name: "testCluster1", + OsImage: "linux", + }, + Timestamp: "", }, } @@ -47,23 +53,30 @@ func Test_eventProducer_filter_created(t *testing.T) { Time: "", Data: workload.Data{ Workload: workload.Workload{ - ClusterName: "testCluster1", - WorkloadName: "testWorkload1", + Name: "testWorkload1", WorkloadType: "deployment", - Containers: workload.Containers{ - Name: "testContainer1", - Image: "testImage1", - Port: "8080", + Labels: map[string]string{ + "k8s-app": "vsm-kubernetes", }, - ServiceName: "serviceName1", - Labels: "k8s-app", - Timestamp: "", - WorkloadProperties: workload.Properties{ + WorkloadProperties: workload.WorkloadProperties{ Schedule: "testSchedule", Replicas: "1", UpdateStrategy: "rollback", + Containers: workload.Containers{ + Name: "testContainer1", + Image: "testImage1", + Port: "8080", + }, }, - }}, + }, + NamespaceName: "namespace-name", + ServiceName: "serviceName1", + Cluster: workload.Cluster{ + Name: "testCluster1", + OsImage: "linux", + }, + Timestamp: "", + }, }, }, }, @@ -83,51 +96,63 @@ func Test_eventProducer_filter_updated_no_change(t *testing.T) { newData := map[string]workload.Data{ "testId1": { Workload: workload.Workload{ - ClusterName: "testCluster1", - WorkloadName: "testWorkload1", + Name: "testWorkload1", WorkloadType: "deployment", - Containers: workload.Containers{ - Name: "testContainer1", - Image: "testImage1", - Port: "8080", + Labels: map[string]string{ + "k8s-app": "vsm-kubernetes", }, - ServiceName: "serviceName1", - Labels: "k8s-app", - Timestamp: "", - WorkloadProperties: workload.Properties{ + WorkloadProperties: workload.WorkloadProperties{ Schedule: "testSchedule", Replicas: "1", UpdateStrategy: "rollback", + Containers: workload.Containers{ + Name: "testContainer1", + Image: "testImage1", + Port: "8080", + }, }, }, + Cluster: workload.Cluster{ + Name: "testCluster1", + OsImage: "linux", + }, + Timestamp: "", + NamespaceName: "namespaceName1", + ServiceName: "serviceName1", }} oldData := map[string]models.DiscoveryEvent{ "testId1": { Body: models.DiscoveryBody{ State: models.State{ - Name: "testCluster1", + Name: "testWorkload1", SourceInstance: "kubernetes", SourceType: "cluster/testCluster1", Time: "2023-05-04T11:03:50+02:00", Data: workload.Data{ Workload: workload.Workload{ - ClusterName: "testCluster1", - WorkloadName: "testWorkload1", + Name: "testWorkload1", WorkloadType: "deployment", - Containers: workload.Containers{ - Name: "testContainer1", - Image: "testImage1", - Port: "8080", + Labels: map[string]string{ + "k8s-app": "vsm-kubernetes", }, - ServiceName: "serviceName1", - Labels: "k8s-app", - Timestamp: "", - WorkloadProperties: workload.Properties{ + WorkloadProperties: workload.WorkloadProperties{ Schedule: "testSchedule", Replicas: "1", UpdateStrategy: "rollback", + Containers: workload.Containers{ + Name: "testContainer1", + Image: "testImage1", + Port: "8080", + }, }, }, + Cluster: workload.Cluster{ + Name: "testCluster1", + OsImage: "linux", + }, + Timestamp: "", + NamespaceName: "namespaceName1", + ServiceName: "serviceName1", }, }, }, @@ -149,23 +174,32 @@ func Test_eventProducer_filter_updated_changed(t *testing.T) { newData := map[string]workload.Data{ "testId1": { Workload: workload.Workload{ - ClusterName: "testClusterName1", - WorkloadName: "testWorkload1", + Name: "testWorkload1", WorkloadType: "deployment", - Containers: workload.Containers{ - Name: "testContainer1", - Image: "testImage1", - Port: "8080", + + Labels: map[string]string{ + "k8s-app": "vsm-kubernetes", }, - ServiceName: "serviceName1", - Labels: "k8s-app", - Timestamp: "", - WorkloadProperties: workload.Properties{ + + WorkloadProperties: workload.WorkloadProperties{ Schedule: "testSchedule", Replicas: "1", UpdateStrategy: "rollback", + Containers: workload.Containers{ + Name: "testContainer1", + Image: "testImage1", + Port: "8080", + }, }, - }}, + }, + Timestamp: "", + ServiceName: "serviceName1", + NamespaceName: "namespace1", + Cluster: workload.Cluster{ + Name: "testClusterName1", + OsImage: "linux", + }, + }, } oldData := map[string]models.DiscoveryEvent{ "testId1": { @@ -177,23 +211,29 @@ func Test_eventProducer_filter_updated_changed(t *testing.T) { Time: "", Data: workload.Data{ Workload: workload.Workload{ - ClusterName: "testCluster1", - WorkloadName: "testWorkload1", + Name: "testWorkload1", WorkloadType: "deployment", - Containers: workload.Containers{ - Name: "testContainer1", - Image: "testImage1", - Port: "8080", + Labels: map[string]string{ + "k8s-app": "vsm-kubernetes", }, - ServiceName: "serviceName1", - Labels: "k8s-app", - Timestamp: "", - WorkloadProperties: workload.Properties{ + WorkloadProperties: workload.WorkloadProperties{ Schedule: "testSchedule", Replicas: "1", UpdateStrategy: "rollback", + Containers: workload.Containers{ + Name: "testContainer1", + Image: "testImage1", + Port: "8080", + }, }, - }}, + }, + Cluster: workload.Cluster{ + Name: "testCluster1", + }, + Timestamp: "", + ServiceName: "serviceName1", + NamespaceName: "namespaceName1", + }, }, }, }, @@ -211,7 +251,7 @@ func Test_eventProducer_filter_updated_changed(t *testing.T) { assert.NoError(t, err) assert.Empty(t, created) assert.Len(t, updated, 1) - assert.Equal(t, "testImage1", parsedData.Workload.Containers.Image) + assert.Equal(t, "testImage1", parsedData.Workload.WorkloadProperties.Containers.Image) assert.Empty(t, filteredData) } @@ -222,60 +262,84 @@ func Test_eventProducer_createECSTEvents(t *testing.T) { id2 := sha256.Sum256([]byte(fmt.Sprintf("%s/%s/%s/%s/%s", "workspace/testWorkspaceId/configuration/testConfigId", models.EventClassWorkload, "testCluster1", "deployment", "testWorkload2"))) id3 := sha256.Sum256([]byte(fmt.Sprintf("%s/%s/%s/%s/%s", "workspace/testWorkspaceId/configuration/testConfigId", models.EventClassWorkload, "testCluster2", "deployment", "testWorkload1"))) id4 := sha256.Sum256([]byte(fmt.Sprintf("%s/%s/%s/%s/%s", "workspace/testWorkspaceId/configuration/testConfigId", models.EventClassWorkload, "testCluster2", "deployment", "testWorkload2"))) - newData := []workload.Workload{ + newData := []workload.Data{ { - ClusterName: "testCluster1", - WorkloadName: "testWorkload1", - WorkloadType: "deployment", - Containers: workload.Containers{ - Name: "testContainer1", - Image: "testImage1", - Port: "8080", + Workload: workload.Workload{ + Name: "testWorkload1", + WorkloadType: "deployment", + Labels: map[string]string{ + "k8s-app": "vsm-kubernetes", + }, + WorkloadProperties: workload.WorkloadProperties{ + Schedule: "testSchedule", + Replicas: "1", + UpdateStrategy: "rollback", + Containers: workload.Containers{ + Name: "testContainer1", + Image: "testImage1", + Port: "8080", + }, + }, }, - ServiceName: "serviceName1", - Labels: "k8s-app", - Timestamp: "", - WorkloadProperties: workload.Properties{ - Schedule: "testSchedule", - Replicas: "1", - UpdateStrategy: "rollback", + ServiceName: "serviceName1", + NamespaceName: "namespaceName1", + Cluster: workload.Cluster{ + Name: "testCluster1", + OsImage: "linux", }, + Timestamp: "", }, { - ClusterName: "testCluster1", - WorkloadName: "testWorkload2", - WorkloadType: "deployment", - Containers: workload.Containers{ - Name: "testContainer2", - Image: "testImage2", - Port: "8080", + Workload: workload.Workload{ + Name: "testWorkload2", + WorkloadType: "deployment", + Labels: map[string]string{ + "k8s-app": "vsm-kubernetes", + }, + WorkloadProperties: workload.WorkloadProperties{ + Schedule: "testSchedule", + Replicas: "1", + UpdateStrategy: "rollback", + Containers: workload.Containers{ + Name: "testContainer2", + Image: "testImage2", + Port: "8080", + }, + }, }, - ServiceName: "serviceName2", - Labels: "k8s-app", - Timestamp: "", - WorkloadProperties: workload.Properties{ - Schedule: "testSchedule", - Replicas: "1", - UpdateStrategy: "rollback", + Cluster: workload.Cluster{ + Name: "testCluster1", + OsImage: "linux", }, + ServiceName: "serviceName2", + NamespaceName: "namespaceName2", + Timestamp: "", }, { - ClusterName: "testCluster2", - WorkloadName: "testWorkload2", - WorkloadType: "deployment", - Containers: workload.Containers{ - Name: "testContainer3", - Image: "testImage3", - Port: "8080", + Workload: workload.Workload{ + Name: "testWorkload2", + WorkloadType: "deployment", + Labels: map[string]string{ + "k8s-app": "vsm-kubernetes", + }, + WorkloadProperties: workload.WorkloadProperties{ + Schedule: "testSchedule", + Replicas: "1", + UpdateStrategy: "rollback", + Containers: workload.Containers{ + Name: "testContainer3", + Image: "testImage3", + Port: "8080", + }, + }, }, - ServiceName: "serviceName3", - Labels: "k8s-app", - Timestamp: "", - WorkloadProperties: workload.Properties{ - Schedule: "testSchedule", - Replicas: "1", - UpdateStrategy: "rollback", + Cluster: workload.Cluster{ + Name: "testCluster2", + OsImage: "linux", }, + ServiceName: "serviceName3", + NamespaceName: "namespaceName3", + Timestamp: "", }, } oldData := []models.DiscoveryEvent{ @@ -292,23 +356,30 @@ func Test_eventProducer_createECSTEvents(t *testing.T) { Time: "", Data: workload.Data{ Workload: workload.Workload{ - ClusterName: "testCluster1", - WorkloadName: "testWorkload2", + Name: "testWorkload2", WorkloadType: "deployment", - Containers: workload.Containers{ - Name: "testContainer1", - Image: "testImage1", - Port: "8080", + Labels: map[string]string{ + "k8s-app": "vsm-kubernetes", }, - ServiceName: "serviceName1", - Labels: "k8s-app", - Timestamp: "", - WorkloadProperties: workload.Properties{ + WorkloadProperties: workload.WorkloadProperties{ Schedule: "testSchedule", Replicas: "1", UpdateStrategy: "rollback", + Containers: workload.Containers{ + Name: "testContainer1", + Image: "testImage1", + Port: "8080", + }, }, - }}, + }, + Cluster: workload.Cluster{ + Name: "testCluster1", + OsImage: "linux", + }, + ServiceName: "serviceName1", + NamespaceName: "namespaceName1", + Timestamp: "", + }, }, }, }, @@ -325,23 +396,30 @@ func Test_eventProducer_createECSTEvents(t *testing.T) { Time: "", Data: workload.Data{ Workload: workload.Workload{ - ClusterName: "testCluster2", - WorkloadName: "testWorkload1", + Name: "testWorkload1", WorkloadType: "deployment", - Containers: workload.Containers{ - Name: "testContainer2", - Image: "testImage2", - Port: "8080", + Labels: map[string]string{ + "k8s-app": "vsm-kubernetes", }, - ServiceName: "serviceName2", - Labels: "k8s-app", - Timestamp: "", - WorkloadProperties: workload.Properties{ + WorkloadProperties: workload.WorkloadProperties{ Schedule: "testSchedule", Replicas: "1", UpdateStrategy: "rollback", + Containers: workload.Containers{ + Name: "testContainer2", + Image: "testImage2", + Port: "8080", + }, }, - }}, + }, + Cluster: workload.Cluster{ + Name: "testCluster2", + OsImage: "linux", + }, + ServiceName: "serviceName2", + NamespaceName: "namespaceName2", + Timestamp: "", + }, }, }, }, @@ -358,23 +436,30 @@ func Test_eventProducer_createECSTEvents(t *testing.T) { Time: "", Data: workload.Data{ Workload: workload.Workload{ - ClusterName: "testCluster2", - WorkloadName: "testWorkload2", + Name: "testWorkload2", WorkloadType: "deployment", - Containers: workload.Containers{ - Name: "testContainer3", - Image: "testImage3", - Port: "8080", + Labels: map[string]string{ + "k8s-app": "vsm-kubernetes", }, - ServiceName: "serviceName3", - Labels: "k8s-app", - Timestamp: "", - WorkloadProperties: workload.Properties{ + WorkloadProperties: workload.WorkloadProperties{ Schedule: "testSchedule", Replicas: "1", UpdateStrategy: "rollback", + Containers: workload.Containers{ + Name: "testContainer3", + Image: "testImage3", + Port: "8080", + }, }, - }}, + }, + Cluster: workload.Cluster{ + Name: "testCluster2", + OsImage: "linux", + }, + ServiceName: "serviceName3", + NamespaceName: "namespaceName3", + Timestamp: "", + }, }, }, }, @@ -407,14 +492,14 @@ func Test_eventProducer_createECSTEvents(t *testing.T) { assert.Equal(t, models.EventActionDeleted, deleted[0].HeaderProperties.Action) parsedData, err = common.ParseWorkloadData(deleted[0]) assert.NoError(t, err) - assert.Equal(t, "testWorkload1", parsedData.Workload.WorkloadName) - assert.Equal(t, "testCluster2", parsedData.Workload.ClusterName) + assert.Equal(t, "testWorkload1", parsedData.Workload.Name) + assert.Equal(t, "testCluster2", parsedData.Cluster.Name) } func Test_eventProducer_processECSTResults_empty(t *testing.T) { mockApi := mocks.NewIrisApi(t) - var newData []workload.Workload + var newData []workload.Data var oldData []models.DiscoveryEvent p := NewEventWorkloadProducer(mockApi, "testRunId", "testWorkspaceId") err := p.ProcessWorkloads(newData, oldData, "testConfigId") diff --git a/pkg/iris/workloads/services/mapper/cronjob.go b/pkg/iris/workloads/services/mapper/cronjob.go index d9eb5b4..ba11be9 100644 --- a/pkg/iris/workloads/services/mapper/cronjob.go +++ b/pkg/iris/workloads/services/mapper/cronjob.go @@ -12,13 +12,13 @@ import ( v1 "k8s.io/api/core/v1" ) -func (m *mapworkload) MapCronJobsEcst(clusterName string, cronJobs *batchv1.CronJobList, services *v1.ServiceList) ([]models.Workload, error) { - var groupedCronJobs []models.Workload +func (m *workloadMapper) MapCronJobsEcst(cluster models.Cluster, cronJobs *batchv1.CronJobList, services *v1.ServiceList) ([]models.Data, error) { + var groupedCronJobs []models.Data for _, cronJob := range cronJobs.Items { // Check if any service has the exact same selector labels and use this as the service related to the deployment cronJobService := ResolveK8sServiceForK8sCronJob(services, cronJob) - mappedCronJob := m.CreateCronjobEcst(clusterName, cronJob, cronJobService) + mappedCronJob := m.CreateCronjobEcst(cluster, cronJob, cronJobService) groupedCronJobs = append(groupedCronJobs, mappedCronJob) } @@ -26,25 +26,32 @@ func (m *mapworkload) MapCronJobsEcst(clusterName string, cronJobs *batchv1.Cron } // CreateCronjobEcst create a data object that contains name, labels, CronJobSchedule and more -func (m *mapworkload) CreateCronjobEcst(clusterName string, cronJob batchv1.CronJob, service string) models.Workload { - mappedDeployment := models.Workload{ - ClusterName: clusterName, - WorkloadType: "cronjob", - WorkloadName: cronJob.Name, - ServiceName: service, - Labels: cronJob.ObjectMeta.Labels, - Timestamp: cronJob.CreationTimestamp.UTC().Format(time.RFC3339), - Containers: models.Containers{ - Name: cronJob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Name, - Image: strings.Split(cronJob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Image, ":")[0], - Port: cronJob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Ports, - K8sLimits: CreateK8sResources(cronJob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Resources.Limits), - K8sRequests: CreateK8sResources(cronJob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Resources.Requests), +func (m *workloadMapper) CreateCronjobEcst(cluster models.Cluster, cronJob batchv1.CronJob, service string) models.Data { + mappedDeployment := models.Data{ + Workload: models.Workload{ + Name: cronJob.Name, + WorkloadType: "cronjob", + Labels: cronJob.ObjectMeta.Labels, + WorkloadProperties: models.WorkloadProperties{ + Replicas: cronJob.Status.String(), + Schedule: cronJob.Spec.Schedule, + Containers: models.Containers{ + Name: cronJob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Name, + Image: strings.Split(cronJob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Image, ":")[0], + Port: cronJob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Ports, + K8sLimits: CreateK8sResources(cronJob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Resources.Limits), + K8sRequests: CreateK8sResources(cronJob.Spec.JobTemplate.Spec.Template.Spec.Containers[0].Resources.Requests), + }, + }, }, - WorkloadProperties: models.Properties{ - Replicas: cronJob.Status.String(), - Schedule: cronJob.Spec.Schedule, + Cluster: models.Cluster{ + Name: cluster.Name, + OsImage: cluster.OsImage, + NoOfNodes: cluster.NoOfNodes, + K8sVersion: cluster.K8sVersion, }, + ServiceName: service, + Timestamp: cronJob.CreationTimestamp.UTC().Format(time.RFC3339), } return mappedDeployment } diff --git a/pkg/iris/workloads/services/mapper/daemonset.go b/pkg/iris/workloads/services/mapper/daemonset.go index 6a224c8..ec69360 100644 --- a/pkg/iris/workloads/services/mapper/daemonset.go +++ b/pkg/iris/workloads/services/mapper/daemonset.go @@ -11,13 +11,13 @@ import ( v1 "k8s.io/api/core/v1" ) -func (m *mapworkload) MapDaemonSetsEcst(clusterName string, daemonSets *appsv1.DaemonSetList, services *v1.ServiceList) ([]workload.Workload, error) { - var allDaemonSets []workload.Workload +func (m *workloadMapper) MapDaemonSetsEcst(cluster workload.Cluster, daemonSets *appsv1.DaemonSetList, services *v1.ServiceList) ([]workload.Data, error) { + var allDaemonSets []workload.Data for _, daemonSet := range daemonSets.Items { // Check if any service has the exact same selector labels and use this as the service related to the deployment daemonSetService := ResolveK8sServiceForK8sDaemonSet(services, daemonSet) - mappedDaemonSet := m.CreateDaemonSetEcst(clusterName, daemonSet, daemonSetService) + mappedDaemonSet := m.CreateDaemonSetEcst(cluster, daemonSet, daemonSetService) allDaemonSets = append(allDaemonSets, mappedDaemonSet) } @@ -25,24 +25,32 @@ func (m *mapworkload) MapDaemonSetsEcst(clusterName string, daemonSets *appsv1.D } // CreateDaemonSetEcst create a data object that contains name, labels, DaemonSet properties and more -func (m *mapworkload) CreateDaemonSetEcst(clusterName string, daemonSet appsv1.DaemonSet, service string) workload.Workload { - mappedDeployment := workload.Workload{ - ClusterName: clusterName, - WorkloadType: "daemonSet", - WorkloadName: daemonSet.Name, - ServiceName: service, - Labels: daemonSet.ObjectMeta.Labels, - Timestamp: daemonSet.CreationTimestamp.UTC().Format(time.RFC3339), - Containers: workload.Containers{ - Name: daemonSet.Spec.Template.Spec.Containers[0].Name, - Image: strings.Split(daemonSet.Spec.Template.Spec.Containers[0].Image, ":")[0], - Port: daemonSet.Spec.Template.Spec.Containers[0].Ports, - K8sLimits: CreateK8sResources(daemonSet.Spec.Template.Spec.Containers[0].Resources.Limits), - K8sRequests: CreateK8sResources(daemonSet.Spec.Template.Spec.Containers[0].Resources.Requests), +func (m *workloadMapper) CreateDaemonSetEcst(cluster workload.Cluster, daemonSet appsv1.DaemonSet, service string) workload.Data { + mappedDeployment := workload.Data{ + Workload: workload.Workload{ + Name: daemonSet.Name, + WorkloadType: "daemonSet", + Labels: daemonSet.ObjectMeta.Labels, + WorkloadProperties: workload.WorkloadProperties{ + UpdateStrategy: string(daemonSet.Spec.UpdateStrategy.Type), + Containers: workload.Containers{ + Name: daemonSet.Spec.Template.Spec.Containers[0].Name, + Image: strings.Split(daemonSet.Spec.Template.Spec.Containers[0].Image, ":")[0], + Port: daemonSet.Spec.Template.Spec.Containers[0].Ports, + K8sLimits: CreateK8sResources(daemonSet.Spec.Template.Spec.Containers[0].Resources.Limits), + K8sRequests: CreateK8sResources(daemonSet.Spec.Template.Spec.Containers[0].Resources.Requests), + }, + }, }, - WorkloadProperties: workload.Properties{ - UpdateStrategy: string(daemonSet.Spec.UpdateStrategy.Type), + Cluster: workload.Cluster{ + Name: cluster.Name, + OsImage: cluster.OsImage, + NoOfNodes: cluster.NoOfNodes, + K8sVersion: cluster.K8sVersion, }, + ServiceName: service, + NamespaceName: daemonSet.Namespace, + Timestamp: daemonSet.CreationTimestamp.UTC().Format(time.RFC3339), } return mappedDeployment } diff --git a/pkg/iris/workloads/services/mapper/deployment.go b/pkg/iris/workloads/services/mapper/deployment.go index 410f582..d6d778f 100644 --- a/pkg/iris/workloads/services/mapper/deployment.go +++ b/pkg/iris/workloads/services/mapper/deployment.go @@ -11,42 +11,50 @@ import ( v1 "k8s.io/api/core/v1" ) -func (m *mapworkload) MapDeploymentsEcst(clusterName string, deployments *appsv1.DeploymentList, services *v1.ServiceList) ([]models.Workload, error) { - var allDeployments []models.Workload +func (m *workloadMapper) MapDeploymentsEcst(cluster models.Cluster, deployments *appsv1.DeploymentList, services *v1.ServiceList) ([]models.Data, error) { + var allDeployments []models.Data for _, deployment := range deployments.Items { deploymentService := "" // Check if any service has the exact same selector labels and use this as the service related to the deployment deploymentService = ResolveK8sServiceForK8sDeployment(services, deployment) - allDeployments = append(allDeployments, m.CreateDeploymentEcst(clusterName, deploymentService, deployment)) + allDeployments = append(allDeployments, m.CreateDeploymentEcst(cluster, deploymentService, deployment)) } return allDeployments, nil } -func (m *mapworkload) CreateDeploymentEcst(clusterName string, deploymentService string, deployment appsv1.Deployment) models.Workload { +func (m *workloadMapper) CreateDeploymentEcst(cluster models.Cluster, deploymentService string, deployment appsv1.Deployment) models.Data { var service = "" if deploymentService != "" { service = deploymentService } - mappedDeployment := models.Workload{ - ClusterName: clusterName, - WorkloadType: "deployment", - WorkloadName: deployment.Name, - ServiceName: service, - Labels: deployment.ObjectMeta.Labels, - Timestamp: deployment.CreationTimestamp.UTC().Format(time.RFC3339), - Containers: models.Containers{ - Name: deployment.Spec.Template.Spec.Containers[0].Name, - Image: deployment.Spec.Template.Spec.Containers[0].Image, - Port: deployment.Spec.Template.Spec.Containers[0].Ports, - K8sLimits: CreateK8sResources(deployment.Spec.Template.Spec.Containers[0].Resources.Limits), - K8sRequests: CreateK8sResources(deployment.Spec.Template.Spec.Containers[0].Resources.Requests), + mappedDeployment := models.Data{ + Workload: models.Workload{ + Name: deployment.Name, + WorkloadType: "deployment", + Labels: deployment.ObjectMeta.Labels, + WorkloadProperties: models.WorkloadProperties{ + Replicas: strconv.FormatInt(int64(deployment.Status.Replicas), 10), + UpdateStrategy: string(deployment.Spec.Strategy.Type), + Containers: models.Containers{ + Name: deployment.Spec.Template.Spec.Containers[0].Name, + Image: deployment.Spec.Template.Spec.Containers[0].Image, + Port: deployment.Spec.Template.Spec.Containers[0].Ports, + K8sLimits: CreateK8sResources(deployment.Spec.Template.Spec.Containers[0].Resources.Limits), + K8sRequests: CreateK8sResources(deployment.Spec.Template.Spec.Containers[0].Resources.Requests), + }, + }, }, - WorkloadProperties: models.Properties{ - Replicas: strconv.FormatInt(int64(deployment.Status.Replicas), 10), - UpdateStrategy: string(deployment.Spec.Strategy.Type), + Cluster: models.Cluster{ + Name: cluster.Name, + OsImage: cluster.OsImage, + NoOfNodes: cluster.NoOfNodes, + K8sVersion: cluster.K8sVersion, }, + ServiceName: service, + NamespaceName: deployment.Namespace, + Timestamp: deployment.CreationTimestamp.UTC().Format(time.RFC3339), } return mappedDeployment } diff --git a/pkg/iris/workloads/services/mapper/mapping.go b/pkg/iris/workloads/services/mapper/mapping.go index b5137a1..f5b6f73 100644 --- a/pkg/iris/workloads/services/mapper/mapping.go +++ b/pkg/iris/workloads/services/mapper/mapping.go @@ -1,15 +1,19 @@ package mapper import ( - "github.com/leanix/leanix-k8s-connector/pkg/iris/workloads/models" + workload "github.com/leanix/leanix-k8s-connector/pkg/iris/workloads/models" "github.com/leanix/leanix-k8s-connector/pkg/kubernetes" + "github.com/leanix/leanix-k8s-connector/pkg/set" + v1 "k8s.io/api/core/v1" + "strings" ) -type MapperWorkload interface { - MapWorkloads(clusterName string) ([]models.Workload, error) +type WorkloadMapper interface { + MapCluster(clusterName string, nodes *v1.NodeList) (workload.Cluster, error) + MapWorkloads(cluster workload.Cluster) ([]workload.Data, error) } -type mapworkload struct { +type workloadMapper struct { KubernetesApi *kubernetes.API ClusterName string WorkspaceId string @@ -20,8 +24,8 @@ func NewMapper( kubernetesApi *kubernetes.API, clusterName string, workspaceId string, - runId string) MapperWorkload { - return &mapworkload{ + runId string) WorkloadMapper { + return &workloadMapper{ KubernetesApi: kubernetesApi, ClusterName: clusterName, WorkspaceId: workspaceId, @@ -29,9 +33,9 @@ func NewMapper( } } -func (m *mapworkload) MapWorkloads(clusterName string) ([]models.Workload, error) { +func (m *workloadMapper) MapWorkloads(cluster workload.Cluster) ([]workload.Data, error) { - var scannedWorkloads []models.Workload + var scannedWorkloads []workload.Data services, err := m.KubernetesApi.Services("") if err != nil { return nil, err @@ -41,7 +45,7 @@ func (m *mapworkload) MapWorkloads(clusterName string) ([]models.Workload, error if err != nil { return nil, err } - mappedDeployments, err := m.MapDeploymentsEcst(clusterName, deployments, services) + mappedDeployments, err := m.MapDeploymentsEcst(cluster, deployments, services) if err != nil { return nil, err } @@ -50,7 +54,7 @@ func (m *mapworkload) MapWorkloads(clusterName string) ([]models.Workload, error if err != nil { return nil, err } - mappedCronJobs, err := m.MapCronJobsEcst(clusterName, cronJobs, services) + mappedCronJobs, err := m.MapCronJobsEcst(cluster, cronJobs, services) if err != nil { return nil, err } @@ -59,7 +63,7 @@ func (m *mapworkload) MapWorkloads(clusterName string) ([]models.Workload, error if err != nil { return nil, err } - MappedStatefulSets, err := m.MapStatefulSetsEcst(clusterName, statefulSets, services) + MappedStatefulSets, err := m.MapStatefulSetsEcst(cluster, statefulSets, services) if err != nil { return nil, err } @@ -68,7 +72,7 @@ func (m *mapworkload) MapWorkloads(clusterName string) ([]models.Workload, error if err != nil { return nil, err } - MappedDaemonSets, err := m.MapDaemonSetsEcst(clusterName, daemonSets, services) + MappedDaemonSets, err := m.MapDaemonSetsEcst(cluster, daemonSets, services) if err != nil { return nil, err } @@ -79,3 +83,25 @@ func (m *mapworkload) MapWorkloads(clusterName string) ([]models.Workload, error scannedWorkloads = append(scannedWorkloads, MappedDaemonSets...) return scannedWorkloads, nil } + +func (m *workloadMapper) MapCluster(clusterName string, nodes *v1.NodeList) (workload.Cluster, error) { + items := nodes.Items + if len(items) == 0 { + return workload.Cluster{ + Name: clusterName, + }, nil + } + os := set.NewStringSet() + k8sVersion := set.NewStringSet() + + for _, n := range items { + os.Add(n.Status.NodeInfo.OSImage) + k8sVersion.Add(n.Status.NodeInfo.KubeletVersion) + } + return workload.Cluster{ + Name: clusterName, + OsImage: strings.Join(os.Items(), ", "), + K8sVersion: strings.Join(k8sVersion.Items(), ", "), + NoOfNodes: len(items), + }, nil +} diff --git a/pkg/iris/workloads/services/mapper/mapping_test.go b/pkg/iris/workloads/services/mapper/mapping_test.go index ce50d40..442c87a 100644 --- a/pkg/iris/workloads/services/mapper/mapping_test.go +++ b/pkg/iris/workloads/services/mapper/mapping_test.go @@ -1,6 +1,7 @@ package mapper import ( + "github.com/leanix/leanix-k8s-connector/pkg/iris/workloads/models" "github.com/leanix/leanix-k8s-connector/pkg/kubernetes" "github.com/stretchr/testify/assert" appsv1 "k8s.io/api/apps/v1" @@ -266,32 +267,36 @@ func Test_MapWorkloads_success(t *testing.T) { mockApi := kubernetes.API{ Client: fake.NewSimpleClientset(append(dummyDeployments, dummyServices...)...), } + testCluster := models.Cluster{ + Name: "testCluster", + OsImage: "linux", + } mapper := NewMapper(&mockApi, "testCluster", "testWorkspace", "testRunId") - results, err := mapper.MapWorkloads("testCluster") + results, err := mapper.MapWorkloads(testCluster) assert.NoError(t, err) assert.NotEmpty(t, results) assert.Equal(t, 5, len(results)) - assert.Equal(t, "test-deployment-1", results[0].WorkloadName) + assert.Equal(t, "test-deployment-1", results[0].Workload.Name) assert.Equal(t, "service-2", results[0].ServiceName) - assert.Equal(t, "deployment", results[1].WorkloadType) + assert.Equal(t, "deployment", results[1].Workload.WorkloadType) // test mapping cronjob - assert.Equal(t, "test-cronjob-1", results[2].WorkloadName) + assert.Equal(t, "test-cronjob-1", results[2].Workload.Name) assert.Equal(t, "service-2", results[2].ServiceName) - assert.Equal(t, "cronjob", results[2].WorkloadType) - assert.Equal(t, "0 0 * * *", results[2].WorkloadProperties.Schedule) + assert.Equal(t, "cronjob", results[2].Workload.WorkloadType) + assert.Equal(t, "0 0 * * *", results[2].Workload.WorkloadProperties.Schedule) // test mapping statefulset - assert.Equal(t, "test-statefulset-1", results[3].WorkloadName) + assert.Equal(t, "test-statefulset-1", results[3].Workload.Name) assert.Equal(t, "service-1", results[3].ServiceName) - assert.Equal(t, "statefulSet", results[3].WorkloadType) - assert.Equal(t, string("\x01"), results[3].WorkloadProperties.Replicas) + assert.Equal(t, "statefulSet", results[3].Workload.WorkloadType) + assert.Equal(t, string("\x01"), results[3].Workload.WorkloadProperties.Replicas) // test mapping daemonset - assert.Equal(t, "test-daemonset-1", results[4].WorkloadName) + assert.Equal(t, "test-daemonset-1", results[4].Workload.Name) assert.Equal(t, "service-1", results[4].ServiceName) - assert.Equal(t, "daemonSet", results[4].WorkloadType) - assert.Equal(t, "50", results[4].Containers.K8sLimits.Memory) + assert.Equal(t, "daemonSet", results[4].Workload.WorkloadType) + assert.Equal(t, "50", results[4].Workload.WorkloadProperties.Containers.K8sLimits.Memory) } diff --git a/pkg/iris/workloads/services/mapper/statefulset.go b/pkg/iris/workloads/services/mapper/statefulset.go index a8751f8..0445757 100644 --- a/pkg/iris/workloads/services/mapper/statefulset.go +++ b/pkg/iris/workloads/services/mapper/statefulset.go @@ -11,13 +11,13 @@ import ( v1 "k8s.io/api/core/v1" ) -func (m *mapworkload) MapStatefulSetsEcst(clusterName string, statefulSets *appsv1.StatefulSetList, services *v1.ServiceList) ([]workload.Workload, error) { - var allStatefulSets []workload.Workload +func (m *workloadMapper) MapStatefulSetsEcst(cluster workload.Cluster, statefulSets *appsv1.StatefulSetList, services *v1.ServiceList) ([]workload.Data, error) { + var allStatefulSets []workload.Data for _, statefulSet := range statefulSets.Items { // Check if any service has the exact same selector labels and use this as the service related to the deployment statefulSetService := ResolveK8sServiceForK8sStatefulSet(services, statefulSet) - mappedStatefulSet := m.CreateStatefulSetEcst(clusterName, statefulSet, statefulSetService) + mappedStatefulSet := m.CreateStatefulSetEcst(cluster, statefulSet, statefulSetService) allStatefulSets = append(allStatefulSets, mappedStatefulSet) } @@ -25,25 +25,33 @@ func (m *mapworkload) MapStatefulSetsEcst(clusterName string, statefulSets *apps } // CreateStatefulSetEcst create a data object that contains name, labels, StatefulSet properties and more -func (m *mapworkload) CreateStatefulSetEcst(clusterName string, statefulSet appsv1.StatefulSet, service string) workload.Workload { - mappedDeployment := workload.Workload{ - ClusterName: clusterName, - WorkloadType: "statefulSet", - WorkloadName: statefulSet.Name, - ServiceName: service, - Labels: statefulSet.ObjectMeta.Labels, - Timestamp: statefulSet.CreationTimestamp.UTC().Format(time.RFC3339), - Containers: workload.Containers{ - Name: statefulSet.Spec.Template.Spec.Containers[0].Name, - Image: strings.Split(statefulSet.Spec.Template.Spec.Containers[0].Image, ":")[0], - Port: statefulSet.Spec.Template.Spec.Containers[0].Ports, - K8sLimits: CreateK8sResources(statefulSet.Spec.Template.Spec.Containers[0].Resources.Limits), - K8sRequests: CreateK8sResources(statefulSet.Spec.Template.Spec.Containers[0].Resources.Requests), +func (m *workloadMapper) CreateStatefulSetEcst(cluster workload.Cluster, statefulSet appsv1.StatefulSet, service string) workload.Data { + mappedDeployment := workload.Data{ + Workload: workload.Workload{ + Name: statefulSet.Name, + WorkloadType: "statefulSet", + Labels: statefulSet.ObjectMeta.Labels, + WorkloadProperties: workload.WorkloadProperties{ + Replicas: string(statefulSet.Status.Replicas), + UpdateStrategy: string(statefulSet.Spec.UpdateStrategy.Type), + Containers: workload.Containers{ + Name: statefulSet.Spec.Template.Spec.Containers[0].Name, + Image: strings.Split(statefulSet.Spec.Template.Spec.Containers[0].Image, ":")[0], + Port: statefulSet.Spec.Template.Spec.Containers[0].Ports, + K8sLimits: CreateK8sResources(statefulSet.Spec.Template.Spec.Containers[0].Resources.Limits), + K8sRequests: CreateK8sResources(statefulSet.Spec.Template.Spec.Containers[0].Resources.Requests), + }, + }, }, - WorkloadProperties: workload.Properties{ - Replicas: string(statefulSet.Status.Replicas), - UpdateStrategy: string(statefulSet.Spec.UpdateStrategy.Type), + Cluster: workload.Cluster{ + Name: cluster.Name, + OsImage: cluster.OsImage, + NoOfNodes: cluster.NoOfNodes, + K8sVersion: cluster.K8sVersion, }, + ServiceName: service, + NamespaceName: statefulSet.Namespace, + Timestamp: statefulSet.CreationTimestamp.UTC().Format(time.RFC3339), } return mappedDeployment }