github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/runtime/client/client.go (about) 1 package client 2 3 import ( 4 "io" 5 "sync" 6 7 pb "github.com/tickoalcantara12/micro/v3/proto/runtime" 8 "github.com/tickoalcantara12/micro/v3/service/client" 9 "github.com/tickoalcantara12/micro/v3/service/context" 10 "github.com/tickoalcantara12/micro/v3/service/runtime" 11 ) 12 13 type svc struct { 14 sync.RWMutex 15 options runtime.Options 16 runtime pb.RuntimeService 17 } 18 19 // Init initializes runtime with given options 20 func (s *svc) Init(opts ...runtime.Option) error { 21 s.Lock() 22 defer s.Unlock() 23 24 for _, o := range opts { 25 o(&s.options) 26 } 27 28 s.runtime = pb.NewRuntimeService("runtime", client.DefaultClient) 29 30 return nil 31 } 32 33 // Create a resource 34 func (s *svc) Create(resource runtime.Resource, opts ...runtime.CreateOption) error { 35 var options runtime.CreateOptions 36 for _, o := range opts { 37 o(&options) 38 } 39 40 // Handle the various different types of resources: 41 switch resource.Type() { 42 case runtime.TypeNamespace: 43 44 // Assert the resource back into a *runtime.Namespace 45 namespace, ok := resource.(*runtime.Namespace) 46 if !ok { 47 return runtime.ErrInvalidResource 48 } 49 50 // Allow the options to take precedence 51 if options.Namespace != "" { 52 namespace.Name = options.Namespace 53 } 54 55 // runtime namespace create request 56 req := &pb.CreateRequest{ 57 Resource: &pb.Resource{ 58 Namespace: &pb.Namespace{ 59 Name: namespace.Name, 60 }, 61 }, 62 } 63 64 if _, err := s.runtime.Create(context.DefaultContext, req, client.WithAuthToken()); err != nil { 65 return err 66 } 67 68 case runtime.TypeNetworkPolicy: 69 70 // Assert the resource back into a *runtime.NetworkPolicy 71 networkPolicy, ok := resource.(*runtime.NetworkPolicy) 72 if !ok { 73 return runtime.ErrInvalidResource 74 } 75 76 // Allow the options to take precedence 77 if options.Namespace != "" { 78 networkPolicy.Namespace = options.Namespace 79 } 80 81 // runtime namespace create request 82 req := &pb.CreateRequest{ 83 Resource: &pb.Resource{ 84 Networkpolicy: &pb.NetworkPolicy{ 85 Name: networkPolicy.Name, 86 Namespace: networkPolicy.Namespace, 87 }, 88 }, 89 } 90 91 if _, err := s.runtime.Create(context.DefaultContext, req, client.WithAuthToken()); err != nil { 92 return err 93 } 94 95 case runtime.TypeResourceQuota: 96 97 // Assert the resource back into a *runtime.ResourceQuota 98 resourceQuota, ok := resource.(*runtime.ResourceQuota) 99 if !ok { 100 return runtime.ErrInvalidResource 101 } 102 103 // Allow the options to take precedence 104 if options.Namespace != "" { 105 resourceQuota.Namespace = options.Namespace 106 } 107 108 // runtime namespace create request 109 req := &pb.CreateRequest{ 110 Resource: &pb.Resource{ 111 Resourcequota: &pb.ResourceQuota{ 112 Requests: &pb.Resources{ 113 CPU: int32(resourceQuota.Requests.CPU), 114 EphemeralStorage: int32(resourceQuota.Requests.Disk), 115 Memory: int32(resourceQuota.Requests.Mem), 116 }, 117 Limits: &pb.Resources{ 118 CPU: int32(resourceQuota.Limits.CPU), 119 EphemeralStorage: int32(resourceQuota.Limits.Disk), 120 Memory: int32(resourceQuota.Limits.Mem), 121 }, 122 Name: resourceQuota.Name, 123 Namespace: resourceQuota.Namespace, 124 }, 125 }, 126 } 127 128 if _, err := s.runtime.Create(context.DefaultContext, req, client.WithAuthToken()); err != nil { 129 return err 130 } 131 132 case runtime.TypeService: 133 134 // Assert the resource back into a *runtime.Service 135 svc, ok := resource.(*runtime.Service) 136 if !ok { 137 return runtime.ErrInvalidResource 138 } 139 140 // set the default source from MICRO_RUNTIME_SOURCE 141 if len(svc.Source) == 0 { 142 svc.Source = s.options.Source 143 } 144 145 // runtime service create request 146 req := &pb.CreateRequest{ 147 Resource: &pb.Resource{ 148 Service: &pb.Service{ 149 Name: svc.Name, 150 Version: svc.Version, 151 Source: svc.Source, 152 Metadata: svc.Metadata, 153 }, 154 }, 155 Options: &pb.CreateOptions{ 156 Command: options.Command, 157 Args: options.Args, 158 Env: options.Env, 159 Type: options.Type, 160 Image: options.Image, 161 Namespace: options.Namespace, 162 Secrets: options.Secrets, 163 Entrypoint: options.Entrypoint, 164 Volumes: options.Volumes, 165 Instances: int64(options.Instances), 166 Force: options.Force, 167 }, 168 } 169 170 if _, err := s.runtime.Create(context.DefaultContext, req, client.WithAuthToken()); err != nil { 171 return err 172 } 173 default: 174 return runtime.ErrInvalidResource 175 } 176 177 return nil 178 } 179 180 func (s *svc) Logs(resource runtime.Resource, options ...runtime.LogsOption) (runtime.LogStream, error) { 181 var opts runtime.LogsOptions 182 for _, o := range options { 183 o(&opts) 184 } 185 186 // Handle the various different types of resources: 187 switch resource.Type() { 188 case runtime.TypeNamespace: 189 // noop (Namespace is not supported by *kubernetes.Logs()) 190 return nil, nil 191 case runtime.TypeNetworkPolicy: 192 // noop (NetworkPolicy is not supported by *kubernetes.Logs()) 193 return nil, nil 194 case runtime.TypeResourceQuota: 195 // noop (ResourceQuota is not supported by *kubernetes.Logs()) 196 return nil, nil 197 case runtime.TypeService: 198 // Assert the resource back into a *runtime.Service 199 service, ok := resource.(*runtime.Service) 200 if !ok { 201 return nil, runtime.ErrInvalidResource 202 } 203 204 ls, err := s.runtime.Logs(context.DefaultContext, &pb.LogsRequest{ 205 Service: service.Name, 206 Version: service.Version, 207 Stream: opts.Stream, 208 Count: opts.Count, 209 Options: &pb.LogsOptions{ 210 Namespace: opts.Namespace, 211 }, 212 }, client.WithAuthToken()) 213 if err != nil { 214 return nil, err 215 } 216 217 logStream := &serviceLogs{ 218 service: service.Name, 219 version: service.Version, 220 stream: make(chan runtime.Log), 221 stop: make(chan bool), 222 } 223 224 go func() { 225 for { 226 select { 227 // @todo this never seems to return, investigate 228 case <-ls.Context().Done(): 229 logStream.Stop() 230 } 231 } 232 }() 233 234 go func() { 235 for { 236 select { 237 // @todo this never seems to return, investigate 238 case <-ls.Context().Done(): 239 close(logStream.stream) 240 return 241 case <-logStream.stop: 242 close(logStream.stream) 243 return 244 default: 245 record := pb.LogRecord{} 246 247 if err := ls.RecvMsg(&record); err != nil { 248 if err != io.EOF { 249 logStream.err = err 250 } 251 close(logStream.stream) 252 logStream.Stop() 253 return 254 } 255 256 logStream.stream <- runtime.Log{ 257 Message: record.GetMessage(), 258 Metadata: record.GetMetadata(), 259 } 260 } 261 } 262 }() 263 264 return logStream, nil 265 default: 266 return nil, runtime.ErrInvalidResource 267 } 268 } 269 270 type serviceLogs struct { 271 service string 272 version string 273 stream chan runtime.Log 274 275 sync.Mutex 276 stop chan bool 277 err error 278 } 279 280 func (l *serviceLogs) Error() error { 281 return l.err 282 } 283 284 func (l *serviceLogs) Chan() chan runtime.Log { 285 return l.stream 286 } 287 288 func (l *serviceLogs) Stop() error { 289 l.Lock() 290 defer l.Unlock() 291 select { 292 case <-l.stop: 293 return nil 294 default: 295 close(l.stop) 296 } 297 return nil 298 } 299 300 // Read returns the service with the given name from the runtime 301 func (s *svc) Read(opts ...runtime.ReadOption) ([]*runtime.Service, error) { 302 var options runtime.ReadOptions 303 for _, o := range opts { 304 o(&options) 305 } 306 307 // runtime service create request 308 req := &pb.ReadRequest{ 309 Options: &pb.ReadOptions{ 310 Service: options.Service, 311 Version: options.Version, 312 Type: options.Type, 313 Namespace: options.Namespace, 314 }, 315 } 316 317 resp, err := s.runtime.Read(context.DefaultContext, req, client.WithAuthToken()) 318 if err != nil { 319 return nil, err 320 } 321 322 services := make([]*runtime.Service, 0, len(resp.Services)) 323 for _, service := range resp.Services { 324 svc := &runtime.Service{ 325 Name: service.Name, 326 Version: service.Version, 327 Source: service.Source, 328 Metadata: service.Metadata, 329 Status: runtime.ServiceStatus(service.Status), 330 } 331 services = append(services, svc) 332 } 333 334 return services, nil 335 } 336 337 // Update a resource 338 func (s *svc) Update(resource runtime.Resource, opts ...runtime.UpdateOption) error { 339 var options runtime.UpdateOptions 340 for _, o := range opts { 341 o(&options) 342 } 343 344 // Handle the various different types of resources: 345 switch resource.Type() { 346 case runtime.TypeNamespace: 347 348 // Assert the resource back into a *runtime.Namespace 349 namespace, ok := resource.(*runtime.Namespace) 350 if !ok { 351 return runtime.ErrInvalidResource 352 } 353 354 // Allow the options to take precedence 355 if options.Namespace != "" { 356 namespace.Name = options.Namespace 357 } 358 359 // runtime namespace update request 360 req := &pb.UpdateRequest{ 361 Resource: &pb.Resource{ 362 Namespace: &pb.Namespace{ 363 Name: namespace.Name, 364 }, 365 }, 366 } 367 368 if _, err := s.runtime.Update(context.DefaultContext, req, client.WithAuthToken()); err != nil { 369 return err 370 } 371 372 case runtime.TypeNetworkPolicy: 373 374 // Assert the resource back into a *runtime.NetworkPolicy 375 networkPolicy, ok := resource.(*runtime.NetworkPolicy) 376 if !ok { 377 return runtime.ErrInvalidResource 378 } 379 380 // Allow the options to take precedence 381 if options.Namespace != "" { 382 networkPolicy.Namespace = options.Namespace 383 } 384 385 // runtime networkpolicy update request 386 req := &pb.UpdateRequest{ 387 Resource: &pb.Resource{ 388 Networkpolicy: &pb.NetworkPolicy{ 389 Name: networkPolicy.Name, 390 Namespace: networkPolicy.Namespace, 391 }, 392 }, 393 } 394 395 if _, err := s.runtime.Update(context.DefaultContext, req, client.WithAuthToken()); err != nil { 396 return err 397 } 398 399 case runtime.TypeResourceQuota: 400 401 // Assert the resource back into a *runtime.ResourceQuota 402 resourceQuota, ok := resource.(*runtime.ResourceQuota) 403 if !ok { 404 return runtime.ErrInvalidResource 405 } 406 407 // Allow the options to take precedence 408 if options.Namespace != "" { 409 resourceQuota.Namespace = options.Namespace 410 } 411 412 // runtime resourcequota update request 413 req := &pb.UpdateRequest{ 414 Resource: &pb.Resource{ 415 Resourcequota: &pb.ResourceQuota{ 416 Requests: &pb.Resources{ 417 CPU: int32(resourceQuota.Requests.CPU), 418 EphemeralStorage: int32(resourceQuota.Requests.Disk), 419 Memory: int32(resourceQuota.Requests.Mem), 420 }, 421 Limits: &pb.Resources{ 422 CPU: int32(resourceQuota.Limits.CPU), 423 EphemeralStorage: int32(resourceQuota.Limits.Disk), 424 Memory: int32(resourceQuota.Limits.Mem), 425 }, 426 Name: resourceQuota.Name, 427 Namespace: resourceQuota.Namespace, 428 }, 429 }, 430 } 431 432 if _, err := s.runtime.Update(context.DefaultContext, req, client.WithAuthToken()); err != nil { 433 return err 434 } 435 436 case runtime.TypeService: 437 438 // Assert the resource back into a *runtime.Service 439 svc, ok := resource.(*runtime.Service) 440 if !ok { 441 return runtime.ErrInvalidResource 442 } 443 444 // runtime service create request 445 req := &pb.UpdateRequest{ 446 Resource: &pb.Resource{ 447 Service: &pb.Service{ 448 Name: svc.Name, 449 Version: svc.Version, 450 Source: svc.Source, 451 Metadata: svc.Metadata, 452 }, 453 }, 454 Options: &pb.UpdateOptions{ 455 Namespace: options.Namespace, 456 Entrypoint: options.Entrypoint, 457 Instances: int64(options.Instances), 458 }, 459 } 460 461 if _, err := s.runtime.Update(context.DefaultContext, req, client.WithAuthToken()); err != nil { 462 return err 463 } 464 default: 465 return runtime.ErrInvalidResource 466 } 467 468 return nil 469 } 470 471 // Delete a resource 472 func (s *svc) Delete(resource runtime.Resource, opts ...runtime.DeleteOption) error { 473 var options runtime.DeleteOptions 474 for _, o := range opts { 475 o(&options) 476 } 477 478 // Handle the various different types of resources: 479 switch resource.Type() { 480 case runtime.TypeNamespace: 481 482 // Assert the resource back into a *runtime.Namespace 483 namespace, ok := resource.(*runtime.Namespace) 484 if !ok { 485 return runtime.ErrInvalidResource 486 } 487 488 // Allow the options to take precedence 489 if options.Namespace != "" { 490 namespace.Name = options.Namespace 491 } 492 493 // runtime namespace delete request 494 req := &pb.DeleteRequest{ 495 Resource: &pb.Resource{ 496 Namespace: &pb.Namespace{ 497 Name: namespace.Name, 498 }, 499 }, 500 } 501 502 if _, err := s.runtime.Delete(context.DefaultContext, req, client.WithAuthToken()); err != nil { 503 return err 504 } 505 506 case runtime.TypeNetworkPolicy: 507 508 // Assert the resource back into a *runtime.NetworkPolicy 509 networkPolicy, ok := resource.(*runtime.NetworkPolicy) 510 if !ok { 511 return runtime.ErrInvalidResource 512 } 513 514 // Allow the options to take precedence 515 if options.Namespace != "" { 516 networkPolicy.Namespace = options.Namespace 517 } 518 519 // runtime namespace delete request 520 req := &pb.DeleteRequest{ 521 Resource: &pb.Resource{ 522 Networkpolicy: &pb.NetworkPolicy{ 523 Name: networkPolicy.Name, 524 Namespace: networkPolicy.Namespace, 525 }, 526 }, 527 } 528 529 if _, err := s.runtime.Delete(context.DefaultContext, req, client.WithAuthToken()); err != nil { 530 return err 531 } 532 533 case runtime.TypeResourceQuota: 534 535 // Assert the resource back into a *runtime.ResourceQuota 536 resourceQuota, ok := resource.(*runtime.ResourceQuota) 537 if !ok { 538 return runtime.ErrInvalidResource 539 } 540 541 // Allow the options to take precedence 542 if options.Namespace != "" { 543 resourceQuota.Namespace = options.Namespace 544 } 545 546 // runtime resourcequota delete request 547 req := &pb.DeleteRequest{ 548 Resource: &pb.Resource{ 549 Resourcequota: &pb.ResourceQuota{ 550 Requests: &pb.Resources{ 551 CPU: int32(resourceQuota.Requests.CPU), 552 EphemeralStorage: int32(resourceQuota.Requests.Disk), 553 Memory: int32(resourceQuota.Requests.Mem), 554 }, 555 Limits: &pb.Resources{ 556 CPU: int32(resourceQuota.Limits.CPU), 557 EphemeralStorage: int32(resourceQuota.Limits.Disk), 558 Memory: int32(resourceQuota.Limits.Mem), 559 }, 560 Name: resourceQuota.Name, 561 Namespace: resourceQuota.Namespace, 562 }, 563 }, 564 } 565 566 if _, err := s.runtime.Delete(context.DefaultContext, req, client.WithAuthToken()); err != nil { 567 return err 568 } 569 570 case runtime.TypeService: 571 572 // Assert the resource back into a *runtime.Service 573 svc, ok := resource.(*runtime.Service) 574 if !ok { 575 return runtime.ErrInvalidResource 576 } 577 578 // runtime service delete request 579 req := &pb.DeleteRequest{ 580 Resource: &pb.Resource{ 581 Service: &pb.Service{ 582 Name: svc.Name, 583 Version: svc.Version, 584 Source: svc.Source, 585 Metadata: svc.Metadata, 586 }, 587 }, 588 Options: &pb.DeleteOptions{ 589 Namespace: options.Namespace, 590 }, 591 } 592 593 if _, err := s.runtime.Delete(context.DefaultContext, req, client.WithAuthToken()); err != nil { 594 return err 595 } 596 default: 597 return runtime.ErrInvalidResource 598 } 599 600 return nil 601 } 602 603 // Start starts the runtime 604 func (s *svc) Start() error { 605 // NOTE: nothing to be done here 606 return nil 607 } 608 609 // Stop stops the runtime 610 func (s *svc) Stop() error { 611 // NOTE: nothing to be done here 612 return nil 613 } 614 615 // Returns the runtime service implementation 616 func (s *svc) String() string { 617 return "service" 618 } 619 620 // NewRuntime creates new service runtime and returns it 621 func NewRuntime(opts ...runtime.Option) runtime.Runtime { 622 var options runtime.Options 623 for _, o := range opts { 624 o(&options) 625 } 626 627 return &svc{ 628 options: options, 629 runtime: pb.NewRuntimeService("runtime", client.DefaultClient), 630 } 631 }