1/* 2Package alils implements the SDK(v0.5.0) of Simple Log Service(abbr. SLS). 3 4For more description about SLS, please read this article: 5http://gitlab.alibaba-inc.com/sls/doc. 6*/ 7package alils 8 9import ( 10 "encoding/json" 11 "fmt" 12 "io/ioutil" 13 "net/http" 14 "net/http/httputil" 15) 16 17// Error message in SLS HTTP response. 18type errorMessage struct { 19 Code string `json:"errorCode"` 20 Message string `json:"errorMessage"` 21} 22 23// LogProject Define the Ali Project detail 24type LogProject struct { 25 Name string // Project name 26 Endpoint string // IP or hostname of SLS endpoint 27 AccessKeyID string 28 AccessKeySecret string 29} 30 31// NewLogProject creates a new SLS project. 32func NewLogProject(name, endpoint, AccessKeyID, accessKeySecret string) (p *LogProject, err error) { 33 p = &LogProject{ 34 Name: name, 35 Endpoint: endpoint, 36 AccessKeyID: AccessKeyID, 37 AccessKeySecret: accessKeySecret, 38 } 39 return p, nil 40} 41 42// ListLogStore returns all logstore names of project p. 43func (p *LogProject) ListLogStore() (storeNames []string, err error) { 44 h := map[string]string{ 45 "x-sls-bodyrawsize": "0", 46 } 47 48 uri := fmt.Sprintf("/logstores") 49 r, err := request(p, "GET", uri, h, nil) 50 if err != nil { 51 return 52 } 53 54 buf, err := ioutil.ReadAll(r.Body) 55 if err != nil { 56 return 57 } 58 59 if r.StatusCode != http.StatusOK { 60 errMsg := &errorMessage{} 61 err = json.Unmarshal(buf, errMsg) 62 if err != nil { 63 err = fmt.Errorf("failed to list logstore") 64 dump, _ := httputil.DumpResponse(r, true) 65 fmt.Printf("%s\n", dump) 66 return 67 } 68 err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) 69 return 70 } 71 72 type Body struct { 73 Count int 74 LogStores []string 75 } 76 body := &Body{} 77 78 err = json.Unmarshal(buf, body) 79 if err != nil { 80 return 81 } 82 83 storeNames = body.LogStores 84 85 return 86} 87 88// GetLogStore returns logstore according by logstore name. 89func (p *LogProject) GetLogStore(name string) (s *LogStore, err error) { 90 h := map[string]string{ 91 "x-sls-bodyrawsize": "0", 92 } 93 94 r, err := request(p, "GET", "/logstores/"+name, h, nil) 95 if err != nil { 96 return 97 } 98 99 buf, err := ioutil.ReadAll(r.Body) 100 if err != nil { 101 return 102 } 103 104 if r.StatusCode != http.StatusOK { 105 errMsg := &errorMessage{} 106 err = json.Unmarshal(buf, errMsg) 107 if err != nil { 108 err = fmt.Errorf("failed to get logstore") 109 dump, _ := httputil.DumpResponse(r, true) 110 fmt.Printf("%s\n", dump) 111 return 112 } 113 err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) 114 return 115 } 116 117 s = &LogStore{} 118 err = json.Unmarshal(buf, s) 119 if err != nil { 120 return 121 } 122 s.project = p 123 return 124} 125 126// CreateLogStore creates a new logstore in SLS, 127// where name is logstore name, 128// and ttl is time-to-live(in day) of logs, 129// and shardCnt is the number of shards. 130func (p *LogProject) CreateLogStore(name string, ttl, shardCnt int) (err error) { 131 132 type Body struct { 133 Name string `json:"logstoreName"` 134 TTL int `json:"ttl"` 135 ShardCount int `json:"shardCount"` 136 } 137 138 store := &Body{ 139 Name: name, 140 TTL: ttl, 141 ShardCount: shardCnt, 142 } 143 144 body, err := json.Marshal(store) 145 if err != nil { 146 return 147 } 148 149 h := map[string]string{ 150 "x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), 151 "Content-Type": "application/json", 152 "Accept-Encoding": "deflate", // TODO: support lz4 153 } 154 155 r, err := request(p, "POST", "/logstores", h, body) 156 if err != nil { 157 return 158 } 159 160 body, err = ioutil.ReadAll(r.Body) 161 if err != nil { 162 return 163 } 164 165 if r.StatusCode != http.StatusOK { 166 errMsg := &errorMessage{} 167 err = json.Unmarshal(body, errMsg) 168 if err != nil { 169 err = fmt.Errorf("failed to create logstore") 170 dump, _ := httputil.DumpResponse(r, true) 171 fmt.Printf("%s\n", dump) 172 return 173 } 174 err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) 175 return 176 } 177 178 return 179} 180 181// DeleteLogStore deletes a logstore according by logstore name. 182func (p *LogProject) DeleteLogStore(name string) (err error) { 183 h := map[string]string{ 184 "x-sls-bodyrawsize": "0", 185 } 186 187 r, err := request(p, "DELETE", "/logstores/"+name, h, nil) 188 if err != nil { 189 return 190 } 191 192 body, err := ioutil.ReadAll(r.Body) 193 if err != nil { 194 return 195 } 196 197 if r.StatusCode != http.StatusOK { 198 errMsg := &errorMessage{} 199 err = json.Unmarshal(body, errMsg) 200 if err != nil { 201 err = fmt.Errorf("failed to delete logstore") 202 dump, _ := httputil.DumpResponse(r, true) 203 fmt.Printf("%s\n", dump) 204 return 205 } 206 err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) 207 return 208 } 209 return 210} 211 212// UpdateLogStore updates a logstore according by logstore name, 213// obviously we can't modify the logstore name itself. 214func (p *LogProject) UpdateLogStore(name string, ttl, shardCnt int) (err error) { 215 216 type Body struct { 217 Name string `json:"logstoreName"` 218 TTL int `json:"ttl"` 219 ShardCount int `json:"shardCount"` 220 } 221 222 store := &Body{ 223 Name: name, 224 TTL: ttl, 225 ShardCount: shardCnt, 226 } 227 228 body, err := json.Marshal(store) 229 if err != nil { 230 return 231 } 232 233 h := map[string]string{ 234 "x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), 235 "Content-Type": "application/json", 236 "Accept-Encoding": "deflate", // TODO: support lz4 237 } 238 239 r, err := request(p, "PUT", "/logstores", h, body) 240 if err != nil { 241 return 242 } 243 244 body, err = ioutil.ReadAll(r.Body) 245 if err != nil { 246 return 247 } 248 249 if r.StatusCode != http.StatusOK { 250 errMsg := &errorMessage{} 251 err = json.Unmarshal(body, errMsg) 252 if err != nil { 253 err = fmt.Errorf("failed to update logstore") 254 dump, _ := httputil.DumpResponse(r, true) 255 fmt.Printf("%s\n", dump) 256 return 257 } 258 err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) 259 return 260 } 261 262 return 263} 264 265// ListMachineGroup returns machine group name list and the total number of machine groups. 266// The offset starts from 0 and the size is the max number of machine groups could be returned. 267func (p *LogProject) ListMachineGroup(offset, size int) (m []string, total int, err error) { 268 h := map[string]string{ 269 "x-sls-bodyrawsize": "0", 270 } 271 272 if size <= 0 { 273 size = 500 274 } 275 276 uri := fmt.Sprintf("/machinegroups?offset=%v&size=%v", offset, size) 277 r, err := request(p, "GET", uri, h, nil) 278 if err != nil { 279 return 280 } 281 282 buf, err := ioutil.ReadAll(r.Body) 283 if err != nil { 284 return 285 } 286 287 if r.StatusCode != http.StatusOK { 288 errMsg := &errorMessage{} 289 err = json.Unmarshal(buf, errMsg) 290 if err != nil { 291 err = fmt.Errorf("failed to list machine group") 292 dump, _ := httputil.DumpResponse(r, true) 293 fmt.Printf("%s\n", dump) 294 return 295 } 296 err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) 297 return 298 } 299 300 type Body struct { 301 MachineGroups []string 302 Count int 303 Total int 304 } 305 body := &Body{} 306 307 err = json.Unmarshal(buf, body) 308 if err != nil { 309 return 310 } 311 312 m = body.MachineGroups 313 total = body.Total 314 315 return 316} 317 318// GetMachineGroup retruns machine group according by machine group name. 319func (p *LogProject) GetMachineGroup(name string) (m *MachineGroup, err error) { 320 h := map[string]string{ 321 "x-sls-bodyrawsize": "0", 322 } 323 324 r, err := request(p, "GET", "/machinegroups/"+name, h, nil) 325 if err != nil { 326 return 327 } 328 329 buf, err := ioutil.ReadAll(r.Body) 330 if err != nil { 331 return 332 } 333 334 if r.StatusCode != http.StatusOK { 335 errMsg := &errorMessage{} 336 err = json.Unmarshal(buf, errMsg) 337 if err != nil { 338 err = fmt.Errorf("failed to get machine group:%v", name) 339 dump, _ := httputil.DumpResponse(r, true) 340 fmt.Printf("%s\n", dump) 341 return 342 } 343 err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) 344 return 345 } 346 347 m = &MachineGroup{} 348 err = json.Unmarshal(buf, m) 349 if err != nil { 350 return 351 } 352 m.project = p 353 return 354} 355 356// CreateMachineGroup creates a new machine group in SLS. 357func (p *LogProject) CreateMachineGroup(m *MachineGroup) (err error) { 358 359 body, err := json.Marshal(m) 360 if err != nil { 361 return 362 } 363 364 h := map[string]string{ 365 "x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), 366 "Content-Type": "application/json", 367 "Accept-Encoding": "deflate", // TODO: support lz4 368 } 369 370 r, err := request(p, "POST", "/machinegroups", h, body) 371 if err != nil { 372 return 373 } 374 375 body, err = ioutil.ReadAll(r.Body) 376 if err != nil { 377 return 378 } 379 380 if r.StatusCode != http.StatusOK { 381 errMsg := &errorMessage{} 382 err = json.Unmarshal(body, errMsg) 383 if err != nil { 384 err = fmt.Errorf("failed to create machine group") 385 dump, _ := httputil.DumpResponse(r, true) 386 fmt.Printf("%s\n", dump) 387 return 388 } 389 err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) 390 return 391 } 392 393 return 394} 395 396// UpdateMachineGroup updates a machine group. 397func (p *LogProject) UpdateMachineGroup(m *MachineGroup) (err error) { 398 399 body, err := json.Marshal(m) 400 if err != nil { 401 return 402 } 403 404 h := map[string]string{ 405 "x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), 406 "Content-Type": "application/json", 407 "Accept-Encoding": "deflate", // TODO: support lz4 408 } 409 410 r, err := request(p, "PUT", "/machinegroups/"+m.Name, h, body) 411 if err != nil { 412 return 413 } 414 415 body, err = ioutil.ReadAll(r.Body) 416 if err != nil { 417 return 418 } 419 420 if r.StatusCode != http.StatusOK { 421 errMsg := &errorMessage{} 422 err = json.Unmarshal(body, errMsg) 423 if err != nil { 424 err = fmt.Errorf("failed to update machine group") 425 dump, _ := httputil.DumpResponse(r, true) 426 fmt.Printf("%s\n", dump) 427 return 428 } 429 err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) 430 return 431 } 432 433 return 434} 435 436// DeleteMachineGroup deletes machine group according machine group name. 437func (p *LogProject) DeleteMachineGroup(name string) (err error) { 438 h := map[string]string{ 439 "x-sls-bodyrawsize": "0", 440 } 441 442 r, err := request(p, "DELETE", "/machinegroups/"+name, h, nil) 443 if err != nil { 444 return 445 } 446 447 body, err := ioutil.ReadAll(r.Body) 448 if err != nil { 449 return 450 } 451 452 if r.StatusCode != http.StatusOK { 453 errMsg := &errorMessage{} 454 err = json.Unmarshal(body, errMsg) 455 if err != nil { 456 err = fmt.Errorf("failed to delete machine group") 457 dump, _ := httputil.DumpResponse(r, true) 458 fmt.Printf("%s\n", dump) 459 return 460 } 461 err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) 462 return 463 } 464 return 465} 466 467// ListConfig returns config names list and the total number of configs. 468// The offset starts from 0 and the size is the max number of configs could be returned. 469func (p *LogProject) ListConfig(offset, size int) (cfgNames []string, total int, err error) { 470 h := map[string]string{ 471 "x-sls-bodyrawsize": "0", 472 } 473 474 if size <= 0 { 475 size = 100 476 } 477 478 uri := fmt.Sprintf("/configs?offset=%v&size=%v", offset, size) 479 r, err := request(p, "GET", uri, h, nil) 480 if err != nil { 481 return 482 } 483 484 buf, err := ioutil.ReadAll(r.Body) 485 if err != nil { 486 return 487 } 488 489 if r.StatusCode != http.StatusOK { 490 errMsg := &errorMessage{} 491 err = json.Unmarshal(buf, errMsg) 492 if err != nil { 493 err = fmt.Errorf("failed to delete machine group") 494 dump, _ := httputil.DumpResponse(r, true) 495 fmt.Printf("%s\n", dump) 496 return 497 } 498 err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) 499 return 500 } 501 502 type Body struct { 503 Total int 504 Configs []string 505 } 506 body := &Body{} 507 508 err = json.Unmarshal(buf, body) 509 if err != nil { 510 return 511 } 512 513 cfgNames = body.Configs 514 total = body.Total 515 return 516} 517 518// GetConfig returns config according by config name. 519func (p *LogProject) GetConfig(name string) (c *LogConfig, err error) { 520 h := map[string]string{ 521 "x-sls-bodyrawsize": "0", 522 } 523 524 r, err := request(p, "GET", "/configs/"+name, h, nil) 525 if err != nil { 526 return 527 } 528 529 buf, err := ioutil.ReadAll(r.Body) 530 if err != nil { 531 return 532 } 533 534 if r.StatusCode != http.StatusOK { 535 errMsg := &errorMessage{} 536 err = json.Unmarshal(buf, errMsg) 537 if err != nil { 538 err = fmt.Errorf("failed to delete config") 539 dump, _ := httputil.DumpResponse(r, true) 540 fmt.Printf("%s\n", dump) 541 return 542 } 543 err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) 544 return 545 } 546 547 c = &LogConfig{} 548 err = json.Unmarshal(buf, c) 549 if err != nil { 550 return 551 } 552 c.project = p 553 return 554} 555 556// UpdateConfig updates a config. 557func (p *LogProject) UpdateConfig(c *LogConfig) (err error) { 558 559 body, err := json.Marshal(c) 560 if err != nil { 561 return 562 } 563 564 h := map[string]string{ 565 "x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), 566 "Content-Type": "application/json", 567 "Accept-Encoding": "deflate", // TODO: support lz4 568 } 569 570 r, err := request(p, "PUT", "/configs/"+c.Name, h, body) 571 if err != nil { 572 return 573 } 574 575 body, err = ioutil.ReadAll(r.Body) 576 if err != nil { 577 return 578 } 579 580 if r.StatusCode != http.StatusOK { 581 errMsg := &errorMessage{} 582 err = json.Unmarshal(body, errMsg) 583 if err != nil { 584 err = fmt.Errorf("failed to update config") 585 dump, _ := httputil.DumpResponse(r, true) 586 fmt.Printf("%s\n", dump) 587 return 588 } 589 err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) 590 return 591 } 592 593 return 594} 595 596// CreateConfig creates a new config in SLS. 597func (p *LogProject) CreateConfig(c *LogConfig) (err error) { 598 599 body, err := json.Marshal(c) 600 if err != nil { 601 return 602 } 603 604 h := map[string]string{ 605 "x-sls-bodyrawsize": fmt.Sprintf("%v", len(body)), 606 "Content-Type": "application/json", 607 "Accept-Encoding": "deflate", // TODO: support lz4 608 } 609 610 r, err := request(p, "POST", "/configs", h, body) 611 if err != nil { 612 return 613 } 614 615 body, err = ioutil.ReadAll(r.Body) 616 if err != nil { 617 return 618 } 619 620 if r.StatusCode != http.StatusOK { 621 errMsg := &errorMessage{} 622 err = json.Unmarshal(body, errMsg) 623 if err != nil { 624 err = fmt.Errorf("failed to update config") 625 dump, _ := httputil.DumpResponse(r, true) 626 fmt.Printf("%s\n", dump) 627 return 628 } 629 err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) 630 return 631 } 632 633 return 634} 635 636// DeleteConfig deletes a config according by config name. 637func (p *LogProject) DeleteConfig(name string) (err error) { 638 h := map[string]string{ 639 "x-sls-bodyrawsize": "0", 640 } 641 642 r, err := request(p, "DELETE", "/configs/"+name, h, nil) 643 if err != nil { 644 return 645 } 646 647 body, err := ioutil.ReadAll(r.Body) 648 if err != nil { 649 return 650 } 651 652 if r.StatusCode != http.StatusOK { 653 errMsg := &errorMessage{} 654 err = json.Unmarshal(body, errMsg) 655 if err != nil { 656 err = fmt.Errorf("failed to delete config") 657 dump, _ := httputil.DumpResponse(r, true) 658 fmt.Printf("%s\n", dump) 659 return 660 } 661 err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) 662 return 663 } 664 return 665} 666 667// GetAppliedMachineGroups returns applied machine group names list according config name. 668func (p *LogProject) GetAppliedMachineGroups(confName string) (groupNames []string, err error) { 669 h := map[string]string{ 670 "x-sls-bodyrawsize": "0", 671 } 672 673 uri := fmt.Sprintf("/configs/%v/machinegroups", confName) 674 r, err := request(p, "GET", uri, h, nil) 675 if err != nil { 676 return 677 } 678 679 buf, err := ioutil.ReadAll(r.Body) 680 if err != nil { 681 return 682 } 683 684 if r.StatusCode != http.StatusOK { 685 errMsg := &errorMessage{} 686 err = json.Unmarshal(buf, errMsg) 687 if err != nil { 688 err = fmt.Errorf("failed to get applied machine groups") 689 dump, _ := httputil.DumpResponse(r, true) 690 fmt.Printf("%s\n", dump) 691 return 692 } 693 err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) 694 return 695 } 696 697 type Body struct { 698 Count int 699 Machinegroups []string 700 } 701 702 body := &Body{} 703 err = json.Unmarshal(buf, body) 704 if err != nil { 705 return 706 } 707 708 groupNames = body.Machinegroups 709 return 710} 711 712// GetAppliedConfigs returns applied config names list according machine group name groupName. 713func (p *LogProject) GetAppliedConfigs(groupName string) (confNames []string, err error) { 714 h := map[string]string{ 715 "x-sls-bodyrawsize": "0", 716 } 717 718 uri := fmt.Sprintf("/machinegroups/%v/configs", groupName) 719 r, err := request(p, "GET", uri, h, nil) 720 if err != nil { 721 return 722 } 723 724 buf, err := ioutil.ReadAll(r.Body) 725 if err != nil { 726 return 727 } 728 729 if r.StatusCode != http.StatusOK { 730 errMsg := &errorMessage{} 731 err = json.Unmarshal(buf, errMsg) 732 if err != nil { 733 err = fmt.Errorf("failed to applied configs") 734 dump, _ := httputil.DumpResponse(r, true) 735 fmt.Printf("%s\n", dump) 736 return 737 } 738 err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) 739 return 740 } 741 742 type Cfg struct { 743 Count int `json:"count"` 744 Configs []string `json:"configs"` 745 } 746 747 body := &Cfg{} 748 err = json.Unmarshal(buf, body) 749 if err != nil { 750 return 751 } 752 753 confNames = body.Configs 754 return 755} 756 757// ApplyConfigToMachineGroup applies config to machine group. 758func (p *LogProject) ApplyConfigToMachineGroup(confName, groupName string) (err error) { 759 h := map[string]string{ 760 "x-sls-bodyrawsize": "0", 761 } 762 763 uri := fmt.Sprintf("/machinegroups/%v/configs/%v", groupName, confName) 764 r, err := request(p, "PUT", uri, h, nil) 765 if err != nil { 766 return 767 } 768 769 buf, err := ioutil.ReadAll(r.Body) 770 if err != nil { 771 return 772 } 773 774 if r.StatusCode != http.StatusOK { 775 errMsg := &errorMessage{} 776 err = json.Unmarshal(buf, errMsg) 777 if err != nil { 778 err = fmt.Errorf("failed to apply config to machine group") 779 dump, _ := httputil.DumpResponse(r, true) 780 fmt.Printf("%s\n", dump) 781 return 782 } 783 err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) 784 return 785 } 786 return 787} 788 789// RemoveConfigFromMachineGroup removes config from machine group. 790func (p *LogProject) RemoveConfigFromMachineGroup(confName, groupName string) (err error) { 791 h := map[string]string{ 792 "x-sls-bodyrawsize": "0", 793 } 794 795 uri := fmt.Sprintf("/machinegroups/%v/configs/%v", groupName, confName) 796 r, err := request(p, "DELETE", uri, h, nil) 797 if err != nil { 798 return 799 } 800 801 buf, err := ioutil.ReadAll(r.Body) 802 if err != nil { 803 return 804 } 805 806 if r.StatusCode != http.StatusOK { 807 errMsg := &errorMessage{} 808 err = json.Unmarshal(buf, errMsg) 809 if err != nil { 810 err = fmt.Errorf("failed to remove config from machine group") 811 dump, _ := httputil.DumpResponse(r, true) 812 fmt.Printf("%s\n", dump) 813 return 814 } 815 err = fmt.Errorf("%v:%v", errMsg.Code, errMsg.Message) 816 return 817 } 818 return 819} 820