@@ -13,6 +13,7 @@ import (
1313
1414 "github.com/docker/docker/api/types/events"
1515 mqtt "github.com/eclipse/paho.mqtt.golang"
16+ "github.com/reubenmiller/go-c8y/pkg/c8y"
1617 "github.com/thin-edge/tedge-container-plugin/pkg/container"
1718 "github.com/thin-edge/tedge-container-plugin/pkg/tedge"
1819)
@@ -68,6 +69,7 @@ type Config struct {
6869 EnableMetrics bool
6970 EnableEngineEvents bool
7071 DeleteFromCloud bool
72+ DeleteOrphans bool
7173
7274 HTTPHost string
7375 HTTPPort uint16
@@ -167,6 +169,42 @@ func (a *App) DeleteLegacyService(deleteFromCloud bool) {
167169 }
168170}
169171
172+ // Delete any unclaimed/orphaned cloud services which haven't been registered with
173+ func (a * App ) DeleteOrphanedCloudServices (tedgeEntities map [string ]tedge.Entity ) error {
174+ extID , _ , err := a .client .CumulocityClient .Identity .GetExternalID (context .Background (), "c8y_Serial" , a .Device .ExternalID ())
175+ if err != nil {
176+ slog .Warn ("Could not lookup device's managed object by its external id." , "err" , err , "externalId" , a .Device .ExternalID ())
177+ }
178+
179+ mos , _ , err := a .client .CumulocityClient .Inventory .GetChildAdditions (context .Background (), extID .ManagedObject .ID , & c8y.ManagedObjectOptions {
180+ Query : fmt .Sprintf ("type eq 'c8y_Service' and (serviceType eq '%s' or serviceType eq '%s')" , container .ContainerType , container .ContainerGroupType ),
181+ PaginationOptions : * c8y .NewPaginationOptions (100 ),
182+ })
183+ if err != nil {
184+ return err
185+ }
186+
187+ slog .Info ("Found cloud services." , "count" , len (mos .References ))
188+
189+ for _ , ref := range mos .References {
190+ target := a .client .Target .Service (ref .ManagedObject .Name )
191+ slog .Info ("Check if service is registered locally." , "topic-id" , target .TopicID )
192+
193+ if _ , found := tedgeEntities [target .TopicID ]; ! found {
194+ slog .Info ("Found orphaned cloud service." , "service" , ref .ManagedObject .Name , "type" , ref .ManagedObject .Type , "moID" , ref .ManagedObject .ID )
195+
196+ if _ , respErr := a .client .CumulocityClient .Inventory .Delete (context .Background (), ref .ManagedObject .ID ); respErr != nil {
197+ slog .Warn ("Could not delete orphaned cloud service." , "err" , respErr )
198+ } else {
199+ slog .Info ("Successfully deleted orphaned cloud service." , "moID" , ref .ManagedObject .ID )
200+ }
201+ } else {
202+ slog .Info ("Service is registered locally." , "topic-id" , target .TopicID )
203+ }
204+ }
205+ return nil
206+ }
207+
170208func (a * App ) Subscribe () error {
171209 topic := tedge .GetTopic (* a .Device .Service ("+" ), "cmd" , "health" , "check" )
172210 slog .Info ("Listening to commands on topic." , "topic" , topic )
@@ -465,19 +503,23 @@ func (a *App) doUpdate(filterOptions container.FilterOptions) error {
465503 delete (existingServices , target .TopicID )
466504
467505 // Register using HTTP API
468- resp , err := tedgeClient . TedgeAPI . CreateEntity ( context . Background (), tedge.Entity {
506+ entity := tedge.Entity {
469507 TedgeType : tedge .EntityTypeService ,
470508 TedgeTopicID : target .TopicID ,
471509 Name : item .Name ,
472510 Type : item .ServiceType ,
473511 TedgeParentID : tedgeClient .Parent .TopicID ,
474- })
512+ }
513+ resp , err := tedgeClient .TedgeAPI .CreateEntity (context .Background (), entity )
475514
476515 if err == nil {
477516 slog .Info ("Registered container." , "topic" , target .Topic (), "url" , resp .RawResponse .Request .URL .String (), "status_code" , resp .RawResponse .Status )
478517 } else {
479518 slog .Error ("Failed to register container." , "topic" , target .Topic (), "err" , err )
480519 }
520+
521+ // Manually add to entities store for re-use later without having to fetch a new list of entities
522+ entities [entity .TedgeTopicID ] = entity
481523 }
482524
483525 // Publish health messages
@@ -530,6 +572,9 @@ func (a *App) doUpdate(filterOptions container.FilterOptions) error {
530572 slog .Warn ("Failed to deregister entity." , "err" , err )
531573 }
532574
575+ // Update entities map
576+ delete (entities , staleTopicID )
577+
533578 // mark targets for deletion from the cloud, but don't delete them yet to give time
534579 // for thin-edge.io to process the status updates
535580 markedForDeletion = append (markedForDeletion , * target )
@@ -556,5 +601,12 @@ func (a *App) doUpdate(filterOptions container.FilterOptions) error {
556601 }
557602 }
558603
604+ // Delete orphaned cloud services
605+ if a .config .DeleteFromCloud && a .config .DeleteOrphans {
606+ if err := a .DeleteOrphanedCloudServices (entities ); err != nil {
607+ slog .Warn ("Could not delete orphaned cloud services." , "err" , err )
608+ }
609+ }
610+
559611 return nil
560612}
0 commit comments