From 723b44bf1afe4f3d8a2c35f92195b0071342cfd5 Mon Sep 17 00:00:00 2001 From: Christian Brunner Date: Fri, 28 Jan 2022 17:30:53 +0100 Subject: [PATCH 1/5] add suport for monthly billing details in excel format --- cmd/billing.go | 446 +++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 67 +------- go.sum | 13 ++ 3 files changed, 460 insertions(+), 66 deletions(-) diff --git a/cmd/billing.go b/cmd/billing.go index 5c9ac8fe..f4d3a30c 100644 --- a/cmd/billing.go +++ b/cmd/billing.go @@ -2,6 +2,8 @@ package cmd import ( "fmt" + "strconv" + "strings" "time" "github.com/fi-ts/cloud-go/api/client/accounting" @@ -12,6 +14,8 @@ import ( "github.com/jinzhu/now" "github.com/spf13/cobra" "github.com/spf13/viper" + + "github.com/xuri/excelize/v2" ) type BillingOpts struct { @@ -24,6 +28,9 @@ type BillingOpts struct { ClusterID string Device string Namespace string + Month string + Year string + Filename string CSV bool } @@ -37,6 +44,21 @@ func newBillingCmd(c *config) *cobra.Command { Short: "manage bills", Long: "TODO", } + excelBillingCmd := &cobra.Command{ + Use: "excel", + Short: "create excel file with monthly billing information for all ressources", + Example: ` + cloudctl billing excel + `, + RunE: func(cmd *cobra.Command, args []string) error { + err := initBillingOpts() + if err != nil { + return err + } + return c.excel() + }, + PreRun: bindPFlags, + } containerBillingCmd := &cobra.Command{ Use: "container", Short: "look at container bills", @@ -162,6 +184,7 @@ func newBillingCmd(c *config) *cobra.Command { PreRun: bindPFlags, } + billingCmd.AddCommand(excelBillingCmd) billingCmd.AddCommand(containerBillingCmd) billingCmd.AddCommand(clusterBillingCmd) billingCmd.AddCommand(ipBillingCmd) @@ -172,6 +195,13 @@ func newBillingCmd(c *config) *cobra.Command { billingOpts = &BillingOpts{} + excelBillingCmd.Flags().StringVarP(&billingOpts.Tenant, "tenant", "t", "", "the tenant to account") + excelBillingCmd.Flags().StringVarP(&billingOpts.Month, "month", "m", "", "requested month") + excelBillingCmd.Flags().StringVarP(&billingOpts.Year, "year", "y", "", "requested year") + excelBillingCmd.Flags().StringVarP(&billingOpts.Filename, "file", "f", "", "excel filename") + + must(viper.BindPFlags(excelBillingCmd.Flags())) + containerBillingCmd.Flags().StringVarP(&billingOpts.Tenant, "tenant", "t", "", "the tenant to account") containerBillingCmd.Flags().StringP("time-format", "", "2006-01-02", "the time format used to parse the arguments 'from' and 'to'") containerBillingCmd.Flags().StringVarP(&billingOpts.FromString, "from", "", "", "the start time in the accounting window to look at (optional, defaults to start of the month") @@ -322,6 +352,422 @@ func (c *config) clusterUsageCSV(cur *models.V1ClusterUsageRequest) error { return nil } +func (c *config) excel() error { + month := int(time.Now().Month()) + year := int(time.Now().Year()) + + var err error + if billingOpts.Month != "" { + if month, err = strconv.Atoi(billingOpts.Month); err != nil { + return err + } + } + if billingOpts.Year != "" { + if year, err = strconv.Atoi(billingOpts.Year); err != nil { + return err + } + } + if billingOpts.Year == "" && int(time.Now().Month()) < month { + year-- + } + + from := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.UTC) + fromDT := strfmt.DateTime(from) + to := time.Date(year, time.Month(month), 1, 0, 0, 0, 0, time.UTC).AddDate(0, 1, 0) + + f := excelize.NewFile() + + f.SetSheetName("Sheet1", "Parameter") + + clusterSheetName := fmt.Sprintf("%04d-%02d", year, month) + " Cluster" + f.NewSheet(clusterSheetName) + containerSheetName := fmt.Sprintf("%04d-%02d", year, month) + " Container" + f.NewSheet(containerSheetName) + volumeSheetName := fmt.Sprintf("%04d-%02d", year, month) + " Volume" + f.NewSheet(volumeSheetName) + ipSheetName := fmt.Sprintf("%04d-%02d", year, month) + " IPs" + f.NewSheet(ipSheetName) + networkSheetName := fmt.Sprintf("%04d-%02d", year, month) + " Network traffic" + f.NewSheet(networkSheetName) + s3SheetName := fmt.Sprintf("%04d-%02d", year, month) + " S3" + f.NewSheet(s3SheetName) + postgresSheetName := fmt.Sprintf("%04d-%02d", year, month) + " Postgres" + f.NewSheet(postgresSheetName) + + // Parameter + f.SetCellValue("Parameter", "A1", "Finance Cloud Native billing") + if billingOpts.Tenant != "" { + f.SetCellValue("Parameter", "A3", "Tenant") + f.SetCellValue("Parameter", "B3", billingOpts.Tenant) + } + f.SetCellValue("Parameter", "A4", "Period Start") + f.SetCellValue("Parameter", "B4", from) + f.SetCellValue("Parameter", "A5", "Period End") + if time.Now().Before(to) { + f.SetCellValue("Parameter", "B5", time.Now()) + } else { + f.SetCellValue("Parameter", "B5", to) + } + f.SetCellValue("Parameter", "A7", "CPU included") + f.SetCellValue("Parameter", "B7", 64) + f.SetCellValue("Parameter", "A8", "RAM included") + f.SetCellValue("Parameter", "B8", 128) + f.SetCellValue("Parameter", "A9", "Local vol included") + f.SetCellValue("Parameter", "B9", 512) + f.SetColWidth("Parameter", "A", "B", 14) + f.SetCellValue("Parameter", "A11", "* Lifetime[s] is always calculated for the given period") + + // Cluster Billing + curCluster := models.V1ClusterUsageRequest{ + From: &fromDT, + To: strfmt.DateTime(to), + } + if billingOpts.Tenant != "" { + curCluster.Tenant = billingOpts.Tenant + } + + requestCluster := accounting.NewClusterUsageParams() + requestCluster.SetBody(&curCluster) + + responseCluster, err := c.cloud.Accounting.ClusterUsage(requestCluster, nil) + if err != nil { + return err + } + + f.SetCellValue(clusterSheetName, "A1", "Tenant") + f.SetCellValue(clusterSheetName, "B1", "Project ID") + f.SetColWidth(clusterSheetName, "B", "B", 0) + f.SetCellValue(clusterSheetName, "C1", "Project") + f.SetCellValue(clusterSheetName, "D1", "Cluster ID") + f.SetColWidth(clusterSheetName, "D", "D", 0) + f.SetCellValue(clusterSheetName, "E1", "Cluster") + f.SetCellValue(clusterSheetName, "F1", "Start") + f.SetColWidth(clusterSheetName, "F", "F", 14) + f.SetCellValue(clusterSheetName, "G1", "Lifetime[s]") + f.SetCellValue(clusterSheetName, "H1", "CPUs used [CPU*h]") + f.SetCellValue(clusterSheetName, "I1", "CPUs on top [CPU*h]") + f.SetCellValue(clusterSheetName, "J1", "CPUs avg [CPU]") + f.SetCellValue(clusterSheetName, "K1", "Memory used [GiB*h]") + f.SetCellValue(clusterSheetName, "L1", "Memory on top [GiB*h]") + f.SetCellValue(clusterSheetName, "M1", "Memory avg [GiB]") + f.SetCellValue(clusterSheetName, "N1", "Local vol used [GiB*h]") + f.SetCellValue(clusterSheetName, "O1", "Local vol on top [GiB*h]") + f.SetCellValue(clusterSheetName, "P1", "Local vol avg [GiB]") + f.SetColWidth(clusterSheetName, "H", "P", 15) + for i, v := range responseCluster.Payload.Usage { + f.SetCellValue(clusterSheetName, "A"+fmt.Sprint(i+2), *v.Tenant) + f.SetCellValue(clusterSheetName, "B"+fmt.Sprint(i+2), *v.Projectid) + f.SetCellValue(clusterSheetName, "C"+fmt.Sprint(i+2), *v.Projectname) + f.SetCellValue(clusterSheetName, "D"+fmt.Sprint(i+2), *v.Clusterid) + f.SetCellValue(clusterSheetName, "E"+fmt.Sprint(i+2), *v.Clustername) + start := time.Time(*v.Clusterstart) + f.SetCellValue(clusterSheetName, "F"+fmt.Sprint(i+2), start) + f.SetCellValue(clusterSheetName, "G"+fmt.Sprint(i+2), *v.Lifetime/1000000000) + f.SetCellFormula(clusterSheetName, "H"+fmt.Sprint(i+2), "=SUMIF('"+containerSheetName+"'!D$1:D$9999,D"+fmt.Sprint(i+2)+",'"+containerSheetName+"'!M$1:M$99999)/3600") + f.SetCellFormula(clusterSheetName, "I"+fmt.Sprint(i+2), "=MAX(H"+fmt.Sprint(i+2)+"*3600-Parameter!B7*G"+fmt.Sprint(i+2)+",0)") + f.SetCellFormula(clusterSheetName, "J"+fmt.Sprint(i+2), "=H"+fmt.Sprint(i+2)+"/G"+fmt.Sprint(i+2)+"*3600") + f.SetCellFormula(clusterSheetName, "K"+fmt.Sprint(i+2), "=SUMIF('"+containerSheetName+"'!D$1:D$9999,D"+fmt.Sprint(i+2)+",'"+containerSheetName+"'!N$1:N$9999)/3600") + f.SetCellFormula(clusterSheetName, "L"+fmt.Sprint(i+2), "=MAX(K"+fmt.Sprint(i+2)+"*3600-Parameter!B8*G"+fmt.Sprint(i+2)+",0)") + f.SetCellFormula(clusterSheetName, "M"+fmt.Sprint(i+2), "=K"+fmt.Sprint(i+2)+"/G"+fmt.Sprint(i+2)+"*3600") + f.SetCellFormula(clusterSheetName, "N"+fmt.Sprint(i+2), "=SUMIF('"+volumeSheetName+"'!D$1:D$9999,D"+fmt.Sprint(i+2)+",'"+volumeSheetName+"'!M$1:M$9999)/3600") + f.SetCellFormula(clusterSheetName, "O"+fmt.Sprint(i+2), "=MAX(N"+fmt.Sprint(i+2)+"*3600-Parameter!B9*G"+fmt.Sprint(i+2)+",0)") + f.SetCellFormula(clusterSheetName, "P"+fmt.Sprint(i+2), "=N"+fmt.Sprint(i+2)+"/G"+fmt.Sprint(i+2)+"*3600") + } + + // Container Billing + curContainer := models.V1ContainerUsageRequest{ + From: &fromDT, + To: strfmt.DateTime(to), + } + if billingOpts.Tenant != "" { + curContainer.Tenant = billingOpts.Tenant + } + + requestContainer := accounting.NewContainerUsageParams() + requestContainer.SetBody(&curContainer) + + responseContainer, err := c.cloud.Accounting.ContainerUsage(requestContainer, nil) + if err != nil { + return err + } + f.SetCellValue(containerSheetName, "A1", "Tenant") + f.SetCellValue(containerSheetName, "B1", "Project ID") + f.SetColWidth(containerSheetName, "B", "B", 0) + f.SetCellValue(containerSheetName, "C1", "Project") + f.SetCellValue(containerSheetName, "D1", "Cluster ID") + f.SetColWidth(containerSheetName, "D", "D", 0) + f.SetCellValue(containerSheetName, "E1", "Cluster") + f.SetCellValue(containerSheetName, "F1", "Namespace") + f.SetCellValue(containerSheetName, "G1", "Pod ID") + f.SetColWidth(containerSheetName, "G", "G", 0) + f.SetCellValue(containerSheetName, "H1", "Podname") + f.SetCellValue(containerSheetName, "I1", "Containername") + f.SetCellValue(containerSheetName, "J1", "Containerimage") + f.SetCellValue(containerSheetName, "K1", "Start") + f.SetColWidth(containerSheetName, "K", "K", 14) + f.SetCellValue(containerSheetName, "L1", "Lifetime[s]") + f.SetCellValue(containerSheetName, "M1", "CPU[CPU*s]") + f.SetCellValue(containerSheetName, "N1", "Memory[GiB*s]") + f.SetCellValue(containerSheetName, "O1", "Annotations") + for i, v := range responseContainer.Payload.Usage { + f.SetCellValue(containerSheetName, "A"+fmt.Sprint(i+2), *v.Tenant) + f.SetCellValue(containerSheetName, "B"+fmt.Sprint(i+2), *v.Projectid) + f.SetCellValue(containerSheetName, "C"+fmt.Sprint(i+2), *v.Projectname) + f.SetCellValue(containerSheetName, "D"+fmt.Sprint(i+2), *v.Clusterid) + f.SetCellValue(containerSheetName, "E"+fmt.Sprint(i+2), *v.Clustername) + f.SetCellValue(containerSheetName, "F"+fmt.Sprint(i+2), *v.Namespace) + f.SetCellValue(containerSheetName, "G"+fmt.Sprint(i+2), *v.Poduuid) + f.SetCellValue(containerSheetName, "H"+fmt.Sprint(i+2), *v.Podname) + f.SetCellValue(containerSheetName, "I"+fmt.Sprint(i+2), *v.Containername) + f.SetCellValue(containerSheetName, "J"+fmt.Sprint(i+2), *v.Containerimage) + start := time.Time(*v.Podstart) + f.SetCellValue(containerSheetName, "K"+fmt.Sprint(i+2), start) + f.SetCellValue(containerSheetName, "L"+fmt.Sprint(i+2), *v.Lifetime/1000000000) + cpuseconds, _ := strconv.Atoi(*v.Cpuseconds) + f.SetCellValue(containerSheetName, "M"+fmt.Sprint(i+2), cpuseconds) + memoryseconds, _ := strconv.Atoi(*v.Memoryseconds) + f.SetCellValue(containerSheetName, "N"+fmt.Sprint(i+2), memoryseconds/1024/1024/1024) + f.SetCellValue(containerSheetName, "O"+fmt.Sprint(i+2), strings.Join(v.Annotations, "; ")) + } + + // Volume Billing + curVolume := models.V1VolumeUsageRequest{ + From: &fromDT, + To: strfmt.DateTime(to), + } + if billingOpts.Tenant != "" { + curVolume.Tenant = billingOpts.Tenant + } + + requestVolume := accounting.NewVolumeUsageParams() + requestVolume.SetBody(&curVolume) + + responseVolume, err := c.cloud.Accounting.VolumeUsage(requestVolume, nil) + if err != nil { + return err + } + f.SetCellValue(volumeSheetName, "A1", "Tenant") + f.SetCellValue(volumeSheetName, "B1", "Project ID") + f.SetColWidth(volumeSheetName, "B", "B", 0) + f.SetCellValue(volumeSheetName, "C1", "Project") + f.SetCellValue(volumeSheetName, "D1", "Cluster ID") + f.SetColWidth(volumeSheetName, "D", "D", 0) + f.SetCellValue(volumeSheetName, "E1", "Cluster") + f.SetCellValue(volumeSheetName, "F1", "Volume ID") + f.SetColWidth(volumeSheetName, "F", "F", 0) + f.SetCellValue(volumeSheetName, "G1", "Volume") + f.SetCellValue(volumeSheetName, "H1", "Class") + f.SetCellValue(volumeSheetName, "I1", "Type") + f.SetCellValue(volumeSheetName, "J1", "Start") + f.SetColWidth(volumeSheetName, "J", "J", 14) + f.SetCellValue(volumeSheetName, "K1", "Lifetime[s]") + f.SetCellValue(volumeSheetName, "L1", "Capacity[GiB*s]") + f.SetCellValue(volumeSheetName, "M1", "Local[GiB*s]") + f.SetCellValue(volumeSheetName, "N1", "Block[GiB*s]") + for i, v := range responseVolume.Payload.Usage { + f.SetCellValue(volumeSheetName, "A"+fmt.Sprint(i+2), *v.Tenant) + f.SetCellValue(volumeSheetName, "B"+fmt.Sprint(i+2), *v.Projectid) + f.SetCellValue(volumeSheetName, "C"+fmt.Sprint(i+2), *v.Projectname) + f.SetCellValue(volumeSheetName, "D"+fmt.Sprint(i+2), *v.Clusterid) + f.SetCellValue(volumeSheetName, "E"+fmt.Sprint(i+2), *v.Clustername) + f.SetCellValue(volumeSheetName, "F"+fmt.Sprint(i+2), *v.UUID) + f.SetCellValue(volumeSheetName, "G"+fmt.Sprint(i+2), *v.Name) + f.SetCellValue(volumeSheetName, "H"+fmt.Sprint(i+2), *v.Class) + f.SetCellValue(volumeSheetName, "I"+fmt.Sprint(i+2), *v.Type) + start := time.Time(*v.Start) + f.SetCellValue(volumeSheetName, "J"+fmt.Sprint(i+2), start) + f.SetCellValue(volumeSheetName, "K"+fmt.Sprint(i+2), *v.Lifetime/1000000000) + capacityseconds, _ := strconv.Atoi(*v.Capacityseconds) + f.SetCellValue(volumeSheetName, "L"+fmt.Sprint(i+2), capacityseconds/1024/1024/1024) + f.SetCellFormula(volumeSheetName, "M"+fmt.Sprint(i+2), "=IF(LEFT(H"+fmt.Sprint(i+2)+",7)=\"csi-lvm\",K"+fmt.Sprint(i+2)+",0)") + f.SetCellFormula(volumeSheetName, "N"+fmt.Sprint(i+2), "=IF(LEFT(H"+fmt.Sprint(i+2)+",10)=\"partition-\",K"+fmt.Sprint(i+2)+",0)") + } + + // IP Billing + curIP := models.V1IPUsageRequest{ + From: &fromDT, + To: strfmt.DateTime(to), + } + if billingOpts.Tenant != "" { + curIP.Tenant = billingOpts.Tenant + } + + requestIP := accounting.NewIPUsageParams() + requestIP.SetBody(&curIP) + + responseIP, err := c.cloud.Accounting.IPUsage(requestIP, nil) + if err != nil { + return err + } + f.SetCellValue(ipSheetName, "A1", "Tenant") + f.SetCellValue(ipSheetName, "B1", "Project ID") + f.SetColWidth(ipSheetName, "B", "B", 0) + f.SetCellValue(ipSheetName, "C1", "Project") + f.SetCellValue(ipSheetName, "D1", "IP") + f.SetColWidth(ipSheetName, "D", "D", 14) + f.SetCellValue(ipSheetName, "E1", "Lifetime[s]") + for i, v := range responseIP.Payload.Usage { + f.SetCellValue(ipSheetName, "A"+fmt.Sprint(i+2), *v.Tenant) + f.SetCellValue(ipSheetName, "B"+fmt.Sprint(i+2), *v.Projectid) + f.SetCellValue(ipSheetName, "C"+fmt.Sprint(i+2), *v.Projectname) + f.SetCellValue(ipSheetName, "D"+fmt.Sprint(i+2), *v.IP) + f.SetCellValue(ipSheetName, "E"+fmt.Sprint(i+2), *v.Lifetime/1000000000) + } + + // network-traffic Billing + curNetwork := models.V1NetworkUsageRequest{ + From: &fromDT, + To: strfmt.DateTime(to), + } + if billingOpts.Tenant != "" { + curNetwork.Tenant = billingOpts.Tenant + } + + requestNetwork := accounting.NewNetworkUsageParams() + requestNetwork.SetBody(&curNetwork) + + responseNetwork, err := c.cloud.Accounting.NetworkUsage(requestNetwork, nil) + if err != nil { + return err + } + f.SetCellValue(networkSheetName, "A1", "Tenant") + f.SetCellValue(networkSheetName, "B1", "Project ID") + f.SetColWidth(networkSheetName, "B", "B", 0) + f.SetCellValue(networkSheetName, "C1", "Project") + f.SetCellValue(networkSheetName, "D1", "Cluster ID") + f.SetColWidth(networkSheetName, "D", "D", 0) + f.SetCellValue(networkSheetName, "E1", "Cluster") + f.SetCellValue(networkSheetName, "F1", "Device") + f.SetCellValue(networkSheetName, "G1", "In[GiB]") + f.SetCellValue(networkSheetName, "H1", "Out[GiB]") + f.SetCellValue(networkSheetName, "I1", "Total[GiB]") + for i, v := range responseNetwork.Payload.Usage { + f.SetCellValue(networkSheetName, "A"+fmt.Sprint(i+2), *v.Tenant) + f.SetCellValue(networkSheetName, "B"+fmt.Sprint(i+2), *v.Projectid) + f.SetCellValue(networkSheetName, "C"+fmt.Sprint(i+2), *v.Projectname) + f.SetCellValue(networkSheetName, "D"+fmt.Sprint(i+2), *v.Clusterid) + f.SetCellValue(networkSheetName, "E"+fmt.Sprint(i+2), *v.Clustername) + f.SetCellValue(networkSheetName, "F"+fmt.Sprint(i+2), *v.Device) + in, _ := strconv.Atoi(*v.In) + f.SetCellValue(networkSheetName, "G"+fmt.Sprint(i+2), in/1024/1024/1024) + out, _ := strconv.Atoi(*v.Out) + f.SetCellValue(networkSheetName, "H"+fmt.Sprint(i+2), out/1024/1024/1024) + total, _ := strconv.Atoi(*v.Total) + f.SetCellValue(networkSheetName, "I"+fmt.Sprint(i+2), total/1024/1024/1024) + } + + // S3 Billing + curS3 := models.V1S3UsageRequest{ + From: &fromDT, + To: strfmt.DateTime(to), + } + if billingOpts.Tenant != "" { + curS3.Tenant = billingOpts.Tenant + } + + requestS3 := accounting.NewS3UsageParams() + requestS3.SetBody(&curS3) + + responseS3, err := c.cloud.Accounting.S3Usage(requestS3, nil) + if err != nil { + return err + } + f.SetCellValue(s3SheetName, "A1", "Tenant") + f.SetCellValue(s3SheetName, "B1", "Project ID") + f.SetColWidth(s3SheetName, "B", "B", 0) + f.SetCellValue(s3SheetName, "C1", "Project") + f.SetCellValue(s3SheetName, "D1", "Partition") + f.SetCellValue(s3SheetName, "E1", "User") + f.SetCellValue(s3SheetName, "F1", "Bucket ID") + f.SetColWidth(s3SheetName, "F", "F", 0) + f.SetCellValue(s3SheetName, "G1", "Bucket") + f.SetCellValue(s3SheetName, "H1", "Objects") + f.SetCellValue(s3SheetName, "I1", "Capacity[GiB*s]") + f.SetCellValue(s3SheetName, "J1", "Lifetime[s]") + for i, v := range responseS3.Payload.Usage { + f.SetCellValue(s3SheetName, "A"+fmt.Sprint(i+2), *v.Tenant) + f.SetCellValue(s3SheetName, "B"+fmt.Sprint(i+2), *v.Projectid) + f.SetCellValue(s3SheetName, "C"+fmt.Sprint(i+2), *v.Projectname) + f.SetCellValue(s3SheetName, "D"+fmt.Sprint(i+2), *v.Partition) + f.SetCellValue(s3SheetName, "E"+fmt.Sprint(i+2), *v.User) + f.SetCellValue(s3SheetName, "F"+fmt.Sprint(i+2), *v.Bucketid) + f.SetCellValue(s3SheetName, "G"+fmt.Sprint(i+2), *v.Bucketname) + objects, _ := strconv.Atoi(*v.Currentnumberofobjects) + f.SetCellValue(s3SheetName, "H"+fmt.Sprint(i+2), objects) + capacityseconds, _ := strconv.Atoi(*v.Storageseconds) + f.SetCellValue(s3SheetName, "I"+fmt.Sprint(i+2), capacityseconds/1024/1024/1024) + f.SetCellValue(s3SheetName, "J"+fmt.Sprint(i+2), *v.Lifetime/1000000000) + } + + // Postgres Billing + curPostgres := models.V1PostgresUsageRequest{ + From: &fromDT, + To: strfmt.DateTime(to), + } + if billingOpts.Tenant != "" { + curPostgres.Tenant = billingOpts.Tenant + } + + requestPostgres := accounting.NewPostgresUsageParams() + requestPostgres.SetBody(&curPostgres) + + responsePostgres, err := c.cloud.Accounting.PostgresUsage(requestPostgres, nil) + if err != nil { + return err + } + f.SetCellValue(postgresSheetName, "A1", "Tenant") + f.SetCellValue(postgresSheetName, "B1", "Project ID") + f.SetColWidth(postgresSheetName, "B", "B", 0) + f.SetCellValue(postgresSheetName, "C1", "Project") + f.SetCellValue(postgresSheetName, "D1", "Postgres ID") + f.SetColWidth(postgresSheetName, "D", "D", 0) + f.SetCellValue(postgresSheetName, "E1", "Description") + f.SetCellValue(postgresSheetName, "F1", "Start") + f.SetColWidth(postgresSheetName, "F", "F", 14) + f.SetCellValue(postgresSheetName, "G1", "CPU[CPU*s]") + f.SetCellValue(postgresSheetName, "H1", "Memory[GiB*s]") + f.SetCellValue(postgresSheetName, "I1", "Storage[GiB*s]") + f.SetCellValue(postgresSheetName, "J1", "Lifetime[s]") + for i, v := range responsePostgres.Payload.Usage { + f.SetCellValue(postgresSheetName, "A"+fmt.Sprint(i+2), *v.Tenant) + f.SetCellValue(postgresSheetName, "B"+fmt.Sprint(i+2), *v.Projectid) + f.SetCellValue(postgresSheetName, "C"+fmt.Sprint(i+2), "") + f.SetCellValue(postgresSheetName, "D"+fmt.Sprint(i+2), *v.Postgresid) + f.SetCellValue(postgresSheetName, "E"+fmt.Sprint(i+2), *v.Postgresdescription) + start := time.Time(*v.Postgresstart) + f.SetCellValue(postgresSheetName, "F"+fmt.Sprint(i+2), start) + cpuseconds, _ := strconv.Atoi(*v.Cpuseconds) + f.SetCellValue(postgresSheetName, "G"+fmt.Sprint(i+2), cpuseconds) + memoryseconds, _ := strconv.Atoi(*v.Memoryseconds) + f.SetCellValue(postgresSheetName, "H"+fmt.Sprint(i+2), memoryseconds/1024/1024/1024) + storageseconds, _ := strconv.Atoi(*v.Storageseconds) + f.SetCellValue(postgresSheetName, "I"+fmt.Sprint(i+2), storageseconds/1024/1024/1024) + f.SetCellValue(postgresSheetName, "J"+fmt.Sprint(i+2), *v.Lifetime/1000000000) + } + + filename := "" + if billingOpts.Filename != "" { + filename = billingOpts.Filename + if !strings.HasSuffix(filename, ".xlsx") { + filename = filename + ".xlsx" + } + } else { + if billingOpts.Tenant != "" { + filename = fmt.Sprintf("%04d-%02d-%s-billing.xlsx", year, month, billingOpts.Tenant) + } else { + filename = fmt.Sprintf("%04d-%02d-billing.xlsx", year, month) + } + } + + if err := f.SaveAs(filename); err != nil { + return err + } + + fmt.Println("Created " + filename) + + return nil +} + func (c *config) containerUsage() error { from := strfmt.DateTime(billingOpts.From) cur := models.V1ContainerUsageRequest{ diff --git a/go.mod b/go.mod index 98292aa6..bbed89a2 100644 --- a/go.mod +++ b/go.mod @@ -31,81 +31,27 @@ require ( ) require ( - github.com/Masterminds/semver v1.5.0 // indirect - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/VividCortex/ewma v1.2.0 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect - github.com/blang/semver/v4 v4.0.0 // indirect - github.com/cheggaaa/pb/v3 v3.0.8 // indirect - github.com/coreos/go-oidc/v3 v3.1.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/emicklei/go-restful-openapi/v2 v2.8.0 // indirect - github.com/emicklei/go-restful/v3 v3.7.3 // indirect github.com/evanphx/json-patch v4.11.0+incompatible // indirect - github.com/fsnotify/fsnotify v1.5.1 // indirect github.com/go-logr/logr v1.2.2 // indirect github.com/go-openapi/analysis v0.21.1 // indirect - github.com/go-openapi/errors v0.20.1 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.6 // indirect - github.com/go-openapi/loads v0.21.0 // indirect - github.com/go-openapi/runtime v0.21.0 // indirect - github.com/go-openapi/spec v0.20.4 // indirect - github.com/go-openapi/swag v0.19.15 // indirect - github.com/go-openapi/validate v0.20.3 // indirect - github.com/go-playground/locales v0.14.0 // indirect - github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/goccy/go-json v0.9.3 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.2.0 // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/google/go-github/v32 v32.1.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/googleapis/gnostic v0.5.1 // indirect - github.com/gorilla/mux v1.8.0 // indirect - github.com/gosimple/unidecode v1.0.1 // indirect - github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/icza/dyno v0.0.0-20210726202311-f1bafe5d9996 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/leodido/go-urn v1.2.1 // indirect - github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect - github.com/lestrrat-go/blackmagic v1.0.0 // indirect - github.com/lestrrat-go/httpcc v1.0.0 // indirect - github.com/lestrrat-go/iter v1.0.1 // indirect github.com/lestrrat-go/jwx v1.2.17 // indirect - github.com/lestrrat-go/option v1.0.0 // indirect - github.com/magiconair/properties v1.8.5 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.12 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect - github.com/metal-stack/security v0.6.3 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect - github.com/mitchellh/mapstructure v1.4.3 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect github.com/nsf/termbox-go v1.1.1 // indirect - github.com/oklog/ulid v1.3.1 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/onsi/gomega v1.17.0 // indirect - github.com/opentracing/opentracing-go v1.2.0 // indirect - github.com/pelletier/go-toml v1.9.4 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/rivo/uniseg v0.2.0 // indirect github.com/spf13/afero v1.8.0 // indirect - github.com/spf13/cast v1.4.1 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.3.0 // indirect - github.com/subosito/gotenv v1.2.0 // indirect + github.com/xuri/excelize/v2 v2.5.0 go.mongodb.org/mongo-driver v1.8.2 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/goleak v1.1.12 // indirect @@ -113,23 +59,12 @@ require ( go.uber.org/zap v1.20.0 // indirect golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce // indirect golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d // indirect - golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect - golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect - google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20220114231437-d2e6a121cae0 // indirect - google.golang.org/grpc v1.43.0 // indirect - google.golang.org/protobuf v1.27.1 // indirect - gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/ini.v1 v1.66.2 // indirect - gopkg.in/square/go-jose.v2 v2.6.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible // indirect k8s.io/klog/v2 v2.40.1 // indirect k8s.io/kube-openapi v0.0.0-20220114203427-a0453230fd26 // indirect - sigs.k8s.io/controller-runtime v0.8.3 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect ) diff --git a/go.sum b/go.sum index f3f51ca2..beed34f6 100644 --- a/go.sum +++ b/go.sum @@ -867,6 +867,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/mreiferson/go-options v1.0.0/go.mod h1:zHtCks/HQvOt8ATyfwVe3JJq2PPuImzXINPRTC03+9w= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= @@ -983,6 +985,10 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= +github.com/richardlehane/mscfb v1.0.3 h1:rD8TBkYWkObWO0oLDFCbwMeZ4KoalxQy+QgniCj3nKI= +github.com/richardlehane/mscfb v1.0.3/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk= +github.com/richardlehane/msoleps v1.0.1 h1:RfrALnSNXzmXLbGct/P2b4xkFz4e8Gmj/0Vj9M9xC1o= +github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -1091,6 +1097,10 @@ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMx github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3 h1:EpI0bqf/eX9SdZDwlMmahKM+CDBgNbsXMhsN28XrM8o= +github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= +github.com/xuri/excelize/v2 v2.5.0 h1:nDDVfX0qaDuGjAvb+5zTd0Bxxoqa1Ffv9B4kiE23PTM= +github.com/xuri/excelize/v2 v2.5.0/go.mod h1:rSu0C3papjzxQA3sdK8cU544TebhrPUoTOaGPIh0Q1A= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1212,6 +1222,8 @@ golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMk golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb h1:fqpd0EBDzlHRCjiphRR5Zo/RSWWQlWv34418dnEixWk= +golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1296,6 +1308,7 @@ golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= From 26ff7bfd800b22a55dc0af360f66dfc09e799b09 Mon Sep 17 00:00:00 2001 From: Christian Brunner Date: Mon, 31 Jan 2022 11:07:46 +0100 Subject: [PATCH 2/5] fixes for the excel formulas --- cmd/billing.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cmd/billing.go b/cmd/billing.go index f4d3a30c..d051d091 100644 --- a/cmd/billing.go +++ b/cmd/billing.go @@ -442,7 +442,7 @@ func (c *config) excel() error { f.SetColWidth(clusterSheetName, "D", "D", 0) f.SetCellValue(clusterSheetName, "E1", "Cluster") f.SetCellValue(clusterSheetName, "F1", "Start") - f.SetColWidth(clusterSheetName, "F", "F", 14) + f.SetColWidth(clusterSheetName, "F", "F", 15) f.SetCellValue(clusterSheetName, "G1", "Lifetime[s]") f.SetCellValue(clusterSheetName, "H1", "CPUs used [CPU*h]") f.SetCellValue(clusterSheetName, "I1", "CPUs on top [CPU*h]") @@ -464,13 +464,13 @@ func (c *config) excel() error { f.SetCellValue(clusterSheetName, "F"+fmt.Sprint(i+2), start) f.SetCellValue(clusterSheetName, "G"+fmt.Sprint(i+2), *v.Lifetime/1000000000) f.SetCellFormula(clusterSheetName, "H"+fmt.Sprint(i+2), "=SUMIF('"+containerSheetName+"'!D$1:D$9999,D"+fmt.Sprint(i+2)+",'"+containerSheetName+"'!M$1:M$99999)/3600") - f.SetCellFormula(clusterSheetName, "I"+fmt.Sprint(i+2), "=MAX(H"+fmt.Sprint(i+2)+"*3600-Parameter!B7*G"+fmt.Sprint(i+2)+",0)") + f.SetCellFormula(clusterSheetName, "I"+fmt.Sprint(i+2), "=MAX(H"+fmt.Sprint(i+2)+"-Parameter!B7*G"+fmt.Sprint(i+2)+"/3600,0)") f.SetCellFormula(clusterSheetName, "J"+fmt.Sprint(i+2), "=H"+fmt.Sprint(i+2)+"/G"+fmt.Sprint(i+2)+"*3600") f.SetCellFormula(clusterSheetName, "K"+fmt.Sprint(i+2), "=SUMIF('"+containerSheetName+"'!D$1:D$9999,D"+fmt.Sprint(i+2)+",'"+containerSheetName+"'!N$1:N$9999)/3600") - f.SetCellFormula(clusterSheetName, "L"+fmt.Sprint(i+2), "=MAX(K"+fmt.Sprint(i+2)+"*3600-Parameter!B8*G"+fmt.Sprint(i+2)+",0)") + f.SetCellFormula(clusterSheetName, "L"+fmt.Sprint(i+2), "=MAX(K"+fmt.Sprint(i+2)+"-Parameter!B8*G"+fmt.Sprint(i+2)+"/3600,0)") f.SetCellFormula(clusterSheetName, "M"+fmt.Sprint(i+2), "=K"+fmt.Sprint(i+2)+"/G"+fmt.Sprint(i+2)+"*3600") f.SetCellFormula(clusterSheetName, "N"+fmt.Sprint(i+2), "=SUMIF('"+volumeSheetName+"'!D$1:D$9999,D"+fmt.Sprint(i+2)+",'"+volumeSheetName+"'!M$1:M$9999)/3600") - f.SetCellFormula(clusterSheetName, "O"+fmt.Sprint(i+2), "=MAX(N"+fmt.Sprint(i+2)+"*3600-Parameter!B9*G"+fmt.Sprint(i+2)+",0)") + f.SetCellFormula(clusterSheetName, "O"+fmt.Sprint(i+2), "=MAX(N"+fmt.Sprint(i+2)+"-Parameter!B9*G"+fmt.Sprint(i+2)+"/3600,0)") f.SetCellFormula(clusterSheetName, "P"+fmt.Sprint(i+2), "=N"+fmt.Sprint(i+2)+"/G"+fmt.Sprint(i+2)+"*3600") } @@ -504,7 +504,7 @@ func (c *config) excel() error { f.SetCellValue(containerSheetName, "I1", "Containername") f.SetCellValue(containerSheetName, "J1", "Containerimage") f.SetCellValue(containerSheetName, "K1", "Start") - f.SetColWidth(containerSheetName, "K", "K", 14) + f.SetColWidth(containerSheetName, "K", "K", 15) f.SetCellValue(containerSheetName, "L1", "Lifetime[s]") f.SetCellValue(containerSheetName, "M1", "CPU[CPU*s]") f.SetCellValue(containerSheetName, "N1", "Memory[GiB*s]") @@ -559,7 +559,7 @@ func (c *config) excel() error { f.SetCellValue(volumeSheetName, "H1", "Class") f.SetCellValue(volumeSheetName, "I1", "Type") f.SetCellValue(volumeSheetName, "J1", "Start") - f.SetColWidth(volumeSheetName, "J", "J", 14) + f.SetColWidth(volumeSheetName, "J", "J", 15) f.SetCellValue(volumeSheetName, "K1", "Lifetime[s]") f.SetCellValue(volumeSheetName, "L1", "Capacity[GiB*s]") f.SetCellValue(volumeSheetName, "M1", "Local[GiB*s]") @@ -579,8 +579,8 @@ func (c *config) excel() error { f.SetCellValue(volumeSheetName, "K"+fmt.Sprint(i+2), *v.Lifetime/1000000000) capacityseconds, _ := strconv.Atoi(*v.Capacityseconds) f.SetCellValue(volumeSheetName, "L"+fmt.Sprint(i+2), capacityseconds/1024/1024/1024) - f.SetCellFormula(volumeSheetName, "M"+fmt.Sprint(i+2), "=IF(LEFT(H"+fmt.Sprint(i+2)+",7)=\"csi-lvm\",K"+fmt.Sprint(i+2)+",0)") - f.SetCellFormula(volumeSheetName, "N"+fmt.Sprint(i+2), "=IF(LEFT(H"+fmt.Sprint(i+2)+",10)=\"partition-\",K"+fmt.Sprint(i+2)+",0)") + f.SetCellFormula(volumeSheetName, "M"+fmt.Sprint(i+2), "=IF(LEFT(H"+fmt.Sprint(i+2)+",7)=\"csi-lvm\",L"+fmt.Sprint(i+2)+",0)") + f.SetCellFormula(volumeSheetName, "N"+fmt.Sprint(i+2), "=IF(LEFT(H"+fmt.Sprint(i+2)+",10)=\"partition-\",L"+fmt.Sprint(i+2)+",0)") } // IP Billing @@ -723,7 +723,7 @@ func (c *config) excel() error { f.SetColWidth(postgresSheetName, "D", "D", 0) f.SetCellValue(postgresSheetName, "E1", "Description") f.SetCellValue(postgresSheetName, "F1", "Start") - f.SetColWidth(postgresSheetName, "F", "F", 14) + f.SetColWidth(postgresSheetName, "F", "F", 15) f.SetCellValue(postgresSheetName, "G1", "CPU[CPU*s]") f.SetCellValue(postgresSheetName, "H1", "Memory[GiB*s]") f.SetCellValue(postgresSheetName, "I1", "Storage[GiB*s]") From cf5c15ea854f120732be9f8f03cf96fc263307b5 Mon Sep 17 00:00:00 2001 From: Christian Brunner Date: Mon, 31 Jan 2022 11:08:15 +0100 Subject: [PATCH 3/5] go mod tidy --- go.mod | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/go.mod b/go.mod index bbed89a2..4c90e803 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,79 @@ require ( sigs.k8s.io/yaml v1.3.0 ) +require ( + github.com/Masterminds/semver v1.5.0 // indirect + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/cheggaaa/pb/v3 v3.0.8 // indirect + github.com/coreos/go-oidc/v3 v3.1.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.7.3 // indirect + github.com/fsnotify/fsnotify v1.5.1 // indirect + github.com/go-openapi/errors v0.20.1 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.6 // indirect + github.com/go-openapi/loads v0.21.0 // indirect + github.com/go-openapi/runtime v0.21.0 // indirect + github.com/go-openapi/spec v0.20.4 // indirect + github.com/go-openapi/swag v0.19.15 // indirect + github.com/go-openapi/validate v0.20.3 // indirect + github.com/go-playground/locales v0.14.0 // indirect + github.com/go-playground/universal-translator v0.18.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt/v4 v4.2.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/go-github/v32 v32.1.0 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/googleapis/gnostic v0.5.1 // indirect + github.com/gorilla/mux v1.8.0 // indirect + github.com/gosimple/unidecode v1.0.1 // indirect + github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/icza/dyno v0.0.0-20210726202311-f1bafe5d9996 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/leodido/go-urn v1.2.1 // indirect + github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect + github.com/lestrrat-go/blackmagic v1.0.0 // indirect + github.com/lestrrat-go/httpcc v1.0.0 // indirect + github.com/lestrrat-go/iter v1.0.1 // indirect + github.com/lestrrat-go/option v1.0.0 // indirect + github.com/magiconair/properties v1.8.5 // indirect + github.com/mattn/go-colorable v0.1.12 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/metal-stack/security v0.6.3 // indirect + github.com/mitchellh/mapstructure v1.4.3 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/oklog/ulid v1.3.1 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/pelletier/go-toml v1.9.4 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/richardlehane/mscfb v1.0.3 // indirect + github.com/richardlehane/msoleps v1.0.1 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/spf13/cast v1.4.1 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.2.0 // indirect + github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3 // indirect + golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect + golang.org/x/text v0.3.7 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/grpc v1.43.0 // indirect + google.golang.org/protobuf v1.27.1 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/ini.v1 v1.66.2 // indirect + gopkg.in/square/go-jose.v2 v2.6.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible // indirect + sigs.k8s.io/controller-runtime v0.8.3 // indirect +) + require ( github.com/VividCortex/ewma v1.2.0 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect From 0782104fb2232fdf12bcbe3ee9f99ac9e70cc8b4 Mon Sep 17 00:00:00 2001 From: Christian Brunner Date: Mon, 7 Feb 2022 08:24:19 +0100 Subject: [PATCH 4/5] Add project-id parameter to excel generation --- cmd/billing.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/cmd/billing.go b/cmd/billing.go index d051d091..ad761d39 100644 --- a/cmd/billing.go +++ b/cmd/billing.go @@ -196,6 +196,7 @@ func newBillingCmd(c *config) *cobra.Command { billingOpts = &BillingOpts{} excelBillingCmd.Flags().StringVarP(&billingOpts.Tenant, "tenant", "t", "", "the tenant to account") + excelBillingCmd.Flags().StringVarP(&billingOpts.ProjectID, "project-id", "p", "", "the project to account") excelBillingCmd.Flags().StringVarP(&billingOpts.Month, "month", "m", "", "requested month") excelBillingCmd.Flags().StringVarP(&billingOpts.Year, "year", "y", "", "requested year") excelBillingCmd.Flags().StringVarP(&billingOpts.Filename, "file", "f", "", "excel filename") @@ -400,6 +401,10 @@ func (c *config) excel() error { f.SetCellValue("Parameter", "A3", "Tenant") f.SetCellValue("Parameter", "B3", billingOpts.Tenant) } + if billingOpts.ProjectID != "" { + f.SetCellValue("Parameter", "C3", "ProjectID") + f.SetCellValue("Parameter", "D3", billingOpts.ProjectID) + } f.SetCellValue("Parameter", "A4", "Period Start") f.SetCellValue("Parameter", "B4", from) f.SetCellValue("Parameter", "A5", "Period End") @@ -425,6 +430,9 @@ func (c *config) excel() error { if billingOpts.Tenant != "" { curCluster.Tenant = billingOpts.Tenant } + if billingOpts.ProjectID != "" { + curCluster.Projectid = billingOpts.ProjectID + } requestCluster := accounting.NewClusterUsageParams() requestCluster.SetBody(&curCluster) @@ -482,6 +490,9 @@ func (c *config) excel() error { if billingOpts.Tenant != "" { curContainer.Tenant = billingOpts.Tenant } + if billingOpts.ProjectID != "" { + curContainer.Projectid = billingOpts.ProjectID + } requestContainer := accounting.NewContainerUsageParams() requestContainer.SetBody(&curContainer) @@ -538,6 +549,9 @@ func (c *config) excel() error { if billingOpts.Tenant != "" { curVolume.Tenant = billingOpts.Tenant } + if billingOpts.ProjectID != "" { + curVolume.Projectid = billingOpts.ProjectID + } requestVolume := accounting.NewVolumeUsageParams() requestVolume.SetBody(&curVolume) @@ -591,6 +605,9 @@ func (c *config) excel() error { if billingOpts.Tenant != "" { curIP.Tenant = billingOpts.Tenant } + if billingOpts.ProjectID != "" { + curIP.Projectid = billingOpts.ProjectID + } requestIP := accounting.NewIPUsageParams() requestIP.SetBody(&curIP) @@ -622,6 +639,9 @@ func (c *config) excel() error { if billingOpts.Tenant != "" { curNetwork.Tenant = billingOpts.Tenant } + if billingOpts.ProjectID != "" { + curNetwork.Tenant = billingOpts.ProjectID + } requestNetwork := accounting.NewNetworkUsageParams() requestNetwork.SetBody(&curNetwork) @@ -664,6 +684,9 @@ func (c *config) excel() error { if billingOpts.Tenant != "" { curS3.Tenant = billingOpts.Tenant } + if billingOpts.ProjectID != "" { + curS3.Projectid = billingOpts.ProjectID + } requestS3 := accounting.NewS3UsageParams() requestS3.SetBody(&curS3) @@ -707,6 +730,9 @@ func (c *config) excel() error { if billingOpts.Tenant != "" { curPostgres.Tenant = billingOpts.Tenant } + if billingOpts.ProjectID != "" { + curPostgres.Projectid = billingOpts.ProjectID + } requestPostgres := accounting.NewPostgresUsageParams() requestPostgres.SetBody(&curPostgres) From 66353fb6a7a88afcc14e08ba05e52a55cc1ebf96 Mon Sep 17 00:00:00 2001 From: Christian Brunner Date: Mon, 7 Feb 2022 08:45:14 +0100 Subject: [PATCH 5/5] Use must() to handle error codes --- cmd/billing.go | 394 ++++++++++++++++++++++++------------------------- 1 file changed, 197 insertions(+), 197 deletions(-) diff --git a/cmd/billing.go b/cmd/billing.go index ad761d39..5802f979 100644 --- a/cmd/billing.go +++ b/cmd/billing.go @@ -396,31 +396,31 @@ func (c *config) excel() error { f.NewSheet(postgresSheetName) // Parameter - f.SetCellValue("Parameter", "A1", "Finance Cloud Native billing") + must(f.SetCellValue("Parameter", "A1", "Finance Cloud Native billing")) if billingOpts.Tenant != "" { - f.SetCellValue("Parameter", "A3", "Tenant") - f.SetCellValue("Parameter", "B3", billingOpts.Tenant) + must(f.SetCellValue("Parameter", "A3", "Tenant")) + must(f.SetCellValue("Parameter", "B3", billingOpts.Tenant)) } if billingOpts.ProjectID != "" { - f.SetCellValue("Parameter", "C3", "ProjectID") - f.SetCellValue("Parameter", "D3", billingOpts.ProjectID) + must(f.SetCellValue("Parameter", "C3", "ProjectID")) + must(f.SetCellValue("Parameter", "D3", billingOpts.ProjectID)) } - f.SetCellValue("Parameter", "A4", "Period Start") - f.SetCellValue("Parameter", "B4", from) - f.SetCellValue("Parameter", "A5", "Period End") + must(f.SetCellValue("Parameter", "A4", "Period Start")) + must(f.SetCellValue("Parameter", "B4", from)) + must(f.SetCellValue("Parameter", "A5", "Period End")) if time.Now().Before(to) { - f.SetCellValue("Parameter", "B5", time.Now()) + must(f.SetCellValue("Parameter", "B5", time.Now())) } else { - f.SetCellValue("Parameter", "B5", to) + must(f.SetCellValue("Parameter", "B5", to)) } - f.SetCellValue("Parameter", "A7", "CPU included") - f.SetCellValue("Parameter", "B7", 64) - f.SetCellValue("Parameter", "A8", "RAM included") - f.SetCellValue("Parameter", "B8", 128) - f.SetCellValue("Parameter", "A9", "Local vol included") - f.SetCellValue("Parameter", "B9", 512) - f.SetColWidth("Parameter", "A", "B", 14) - f.SetCellValue("Parameter", "A11", "* Lifetime[s] is always calculated for the given period") + must(f.SetCellValue("Parameter", "A7", "CPU included")) + must(f.SetCellValue("Parameter", "B7", 64)) + must(f.SetCellValue("Parameter", "A8", "RAM included")) + must(f.SetCellValue("Parameter", "B8", 128)) + must(f.SetCellValue("Parameter", "A9", "Local vol included")) + must(f.SetCellValue("Parameter", "B9", 512)) + must(f.SetColWidth("Parameter", "A", "B", 14)) + must(f.SetCellValue("Parameter", "A11", "* Lifetime[s] is always calculated for the given period")) // Cluster Billing curCluster := models.V1ClusterUsageRequest{ @@ -442,44 +442,44 @@ func (c *config) excel() error { return err } - f.SetCellValue(clusterSheetName, "A1", "Tenant") - f.SetCellValue(clusterSheetName, "B1", "Project ID") - f.SetColWidth(clusterSheetName, "B", "B", 0) - f.SetCellValue(clusterSheetName, "C1", "Project") - f.SetCellValue(clusterSheetName, "D1", "Cluster ID") - f.SetColWidth(clusterSheetName, "D", "D", 0) - f.SetCellValue(clusterSheetName, "E1", "Cluster") - f.SetCellValue(clusterSheetName, "F1", "Start") - f.SetColWidth(clusterSheetName, "F", "F", 15) - f.SetCellValue(clusterSheetName, "G1", "Lifetime[s]") - f.SetCellValue(clusterSheetName, "H1", "CPUs used [CPU*h]") - f.SetCellValue(clusterSheetName, "I1", "CPUs on top [CPU*h]") - f.SetCellValue(clusterSheetName, "J1", "CPUs avg [CPU]") - f.SetCellValue(clusterSheetName, "K1", "Memory used [GiB*h]") - f.SetCellValue(clusterSheetName, "L1", "Memory on top [GiB*h]") - f.SetCellValue(clusterSheetName, "M1", "Memory avg [GiB]") - f.SetCellValue(clusterSheetName, "N1", "Local vol used [GiB*h]") - f.SetCellValue(clusterSheetName, "O1", "Local vol on top [GiB*h]") - f.SetCellValue(clusterSheetName, "P1", "Local vol avg [GiB]") - f.SetColWidth(clusterSheetName, "H", "P", 15) + must(f.SetCellValue(clusterSheetName, "A1", "Tenant")) + must(f.SetCellValue(clusterSheetName, "B1", "Project ID")) + must(f.SetColWidth(clusterSheetName, "B", "B", 0)) + must(f.SetCellValue(clusterSheetName, "C1", "Project")) + must(f.SetCellValue(clusterSheetName, "D1", "Cluster ID")) + must(f.SetColWidth(clusterSheetName, "D", "D", 0)) + must(f.SetCellValue(clusterSheetName, "E1", "Cluster")) + must(f.SetCellValue(clusterSheetName, "F1", "Start")) + must(f.SetColWidth(clusterSheetName, "F", "F", 15)) + must(f.SetCellValue(clusterSheetName, "G1", "Lifetime[s]")) + must(f.SetCellValue(clusterSheetName, "H1", "CPUs used [CPU*h]")) + must(f.SetCellValue(clusterSheetName, "I1", "CPUs on top [CPU*h]")) + must(f.SetCellValue(clusterSheetName, "J1", "CPUs avg [CPU]")) + must(f.SetCellValue(clusterSheetName, "K1", "Memory used [GiB*h]")) + must(f.SetCellValue(clusterSheetName, "L1", "Memory on top [GiB*h]")) + must(f.SetCellValue(clusterSheetName, "M1", "Memory avg [GiB]")) + must(f.SetCellValue(clusterSheetName, "N1", "Local vol used [GiB*h]")) + must(f.SetCellValue(clusterSheetName, "O1", "Local vol on top [GiB*h]")) + must(f.SetCellValue(clusterSheetName, "P1", "Local vol avg [GiB]")) + must(f.SetColWidth(clusterSheetName, "H", "P", 15)) for i, v := range responseCluster.Payload.Usage { - f.SetCellValue(clusterSheetName, "A"+fmt.Sprint(i+2), *v.Tenant) - f.SetCellValue(clusterSheetName, "B"+fmt.Sprint(i+2), *v.Projectid) - f.SetCellValue(clusterSheetName, "C"+fmt.Sprint(i+2), *v.Projectname) - f.SetCellValue(clusterSheetName, "D"+fmt.Sprint(i+2), *v.Clusterid) - f.SetCellValue(clusterSheetName, "E"+fmt.Sprint(i+2), *v.Clustername) + must(f.SetCellValue(clusterSheetName, "A"+fmt.Sprint(i+2), *v.Tenant)) + must(f.SetCellValue(clusterSheetName, "B"+fmt.Sprint(i+2), *v.Projectid)) + must(f.SetCellValue(clusterSheetName, "C"+fmt.Sprint(i+2), *v.Projectname)) + must(f.SetCellValue(clusterSheetName, "D"+fmt.Sprint(i+2), *v.Clusterid)) + must(f.SetCellValue(clusterSheetName, "E"+fmt.Sprint(i+2), *v.Clustername)) start := time.Time(*v.Clusterstart) - f.SetCellValue(clusterSheetName, "F"+fmt.Sprint(i+2), start) - f.SetCellValue(clusterSheetName, "G"+fmt.Sprint(i+2), *v.Lifetime/1000000000) - f.SetCellFormula(clusterSheetName, "H"+fmt.Sprint(i+2), "=SUMIF('"+containerSheetName+"'!D$1:D$9999,D"+fmt.Sprint(i+2)+",'"+containerSheetName+"'!M$1:M$99999)/3600") - f.SetCellFormula(clusterSheetName, "I"+fmt.Sprint(i+2), "=MAX(H"+fmt.Sprint(i+2)+"-Parameter!B7*G"+fmt.Sprint(i+2)+"/3600,0)") - f.SetCellFormula(clusterSheetName, "J"+fmt.Sprint(i+2), "=H"+fmt.Sprint(i+2)+"/G"+fmt.Sprint(i+2)+"*3600") - f.SetCellFormula(clusterSheetName, "K"+fmt.Sprint(i+2), "=SUMIF('"+containerSheetName+"'!D$1:D$9999,D"+fmt.Sprint(i+2)+",'"+containerSheetName+"'!N$1:N$9999)/3600") - f.SetCellFormula(clusterSheetName, "L"+fmt.Sprint(i+2), "=MAX(K"+fmt.Sprint(i+2)+"-Parameter!B8*G"+fmt.Sprint(i+2)+"/3600,0)") - f.SetCellFormula(clusterSheetName, "M"+fmt.Sprint(i+2), "=K"+fmt.Sprint(i+2)+"/G"+fmt.Sprint(i+2)+"*3600") - f.SetCellFormula(clusterSheetName, "N"+fmt.Sprint(i+2), "=SUMIF('"+volumeSheetName+"'!D$1:D$9999,D"+fmt.Sprint(i+2)+",'"+volumeSheetName+"'!M$1:M$9999)/3600") - f.SetCellFormula(clusterSheetName, "O"+fmt.Sprint(i+2), "=MAX(N"+fmt.Sprint(i+2)+"-Parameter!B9*G"+fmt.Sprint(i+2)+"/3600,0)") - f.SetCellFormula(clusterSheetName, "P"+fmt.Sprint(i+2), "=N"+fmt.Sprint(i+2)+"/G"+fmt.Sprint(i+2)+"*3600") + must(f.SetCellValue(clusterSheetName, "F"+fmt.Sprint(i+2), start)) + must(f.SetCellValue(clusterSheetName, "G"+fmt.Sprint(i+2), *v.Lifetime/1000000000)) + must(f.SetCellFormula(clusterSheetName, "H"+fmt.Sprint(i+2), "=SUMIF('"+containerSheetName+"'!D$1:D$9999,D"+fmt.Sprint(i+2)+",'"+containerSheetName+"'!M$1:M$99999)/3600")) + must(f.SetCellFormula(clusterSheetName, "I"+fmt.Sprint(i+2), "=MAX(H"+fmt.Sprint(i+2)+"-Parameter!B7*G"+fmt.Sprint(i+2)+"/3600,0)")) + must(f.SetCellFormula(clusterSheetName, "J"+fmt.Sprint(i+2), "=H"+fmt.Sprint(i+2)+"/G"+fmt.Sprint(i+2)+"*3600")) + must(f.SetCellFormula(clusterSheetName, "K"+fmt.Sprint(i+2), "=SUMIF('"+containerSheetName+"'!D$1:D$9999,D"+fmt.Sprint(i+2)+",'"+containerSheetName+"'!N$1:N$9999)/3600")) + must(f.SetCellFormula(clusterSheetName, "L"+fmt.Sprint(i+2), "=MAX(K"+fmt.Sprint(i+2)+"-Parameter!B8*G"+fmt.Sprint(i+2)+"/3600,0)")) + must(f.SetCellFormula(clusterSheetName, "M"+fmt.Sprint(i+2), "=K"+fmt.Sprint(i+2)+"/G"+fmt.Sprint(i+2)+"*3600")) + must(f.SetCellFormula(clusterSheetName, "N"+fmt.Sprint(i+2), "=SUMIF('"+volumeSheetName+"'!D$1:D$9999,D"+fmt.Sprint(i+2)+",'"+volumeSheetName+"'!M$1:M$9999)/3600")) + must(f.SetCellFormula(clusterSheetName, "O"+fmt.Sprint(i+2), "=MAX(N"+fmt.Sprint(i+2)+"-Parameter!B9*G"+fmt.Sprint(i+2)+"/3600,0)")) + must(f.SetCellFormula(clusterSheetName, "P"+fmt.Sprint(i+2), "=N"+fmt.Sprint(i+2)+"/G"+fmt.Sprint(i+2)+"*3600")) } // Container Billing @@ -501,44 +501,44 @@ func (c *config) excel() error { if err != nil { return err } - f.SetCellValue(containerSheetName, "A1", "Tenant") - f.SetCellValue(containerSheetName, "B1", "Project ID") - f.SetColWidth(containerSheetName, "B", "B", 0) - f.SetCellValue(containerSheetName, "C1", "Project") - f.SetCellValue(containerSheetName, "D1", "Cluster ID") - f.SetColWidth(containerSheetName, "D", "D", 0) - f.SetCellValue(containerSheetName, "E1", "Cluster") - f.SetCellValue(containerSheetName, "F1", "Namespace") - f.SetCellValue(containerSheetName, "G1", "Pod ID") - f.SetColWidth(containerSheetName, "G", "G", 0) - f.SetCellValue(containerSheetName, "H1", "Podname") - f.SetCellValue(containerSheetName, "I1", "Containername") - f.SetCellValue(containerSheetName, "J1", "Containerimage") - f.SetCellValue(containerSheetName, "K1", "Start") - f.SetColWidth(containerSheetName, "K", "K", 15) - f.SetCellValue(containerSheetName, "L1", "Lifetime[s]") - f.SetCellValue(containerSheetName, "M1", "CPU[CPU*s]") - f.SetCellValue(containerSheetName, "N1", "Memory[GiB*s]") - f.SetCellValue(containerSheetName, "O1", "Annotations") + must(f.SetCellValue(containerSheetName, "A1", "Tenant")) + must(f.SetCellValue(containerSheetName, "B1", "Project ID")) + must(f.SetColWidth(containerSheetName, "B", "B", 0)) + must(f.SetCellValue(containerSheetName, "C1", "Project")) + must(f.SetCellValue(containerSheetName, "D1", "Cluster ID")) + must(f.SetColWidth(containerSheetName, "D", "D", 0)) + must(f.SetCellValue(containerSheetName, "E1", "Cluster")) + must(f.SetCellValue(containerSheetName, "F1", "Namespace")) + must(f.SetCellValue(containerSheetName, "G1", "Pod ID")) + must(f.SetColWidth(containerSheetName, "G", "G", 0)) + must(f.SetCellValue(containerSheetName, "H1", "Podname")) + must(f.SetCellValue(containerSheetName, "I1", "Containername")) + must(f.SetCellValue(containerSheetName, "J1", "Containerimage")) + must(f.SetCellValue(containerSheetName, "K1", "Start")) + must(f.SetColWidth(containerSheetName, "K", "K", 15)) + must(f.SetCellValue(containerSheetName, "L1", "Lifetime[s]")) + must(f.SetCellValue(containerSheetName, "M1", "CPU[CPU*s]")) + must(f.SetCellValue(containerSheetName, "N1", "Memory[GiB*s]")) + must(f.SetCellValue(containerSheetName, "O1", "Annotations")) for i, v := range responseContainer.Payload.Usage { - f.SetCellValue(containerSheetName, "A"+fmt.Sprint(i+2), *v.Tenant) - f.SetCellValue(containerSheetName, "B"+fmt.Sprint(i+2), *v.Projectid) - f.SetCellValue(containerSheetName, "C"+fmt.Sprint(i+2), *v.Projectname) - f.SetCellValue(containerSheetName, "D"+fmt.Sprint(i+2), *v.Clusterid) - f.SetCellValue(containerSheetName, "E"+fmt.Sprint(i+2), *v.Clustername) - f.SetCellValue(containerSheetName, "F"+fmt.Sprint(i+2), *v.Namespace) - f.SetCellValue(containerSheetName, "G"+fmt.Sprint(i+2), *v.Poduuid) - f.SetCellValue(containerSheetName, "H"+fmt.Sprint(i+2), *v.Podname) - f.SetCellValue(containerSheetName, "I"+fmt.Sprint(i+2), *v.Containername) - f.SetCellValue(containerSheetName, "J"+fmt.Sprint(i+2), *v.Containerimage) + must(f.SetCellValue(containerSheetName, "A"+fmt.Sprint(i+2), *v.Tenant)) + must(f.SetCellValue(containerSheetName, "B"+fmt.Sprint(i+2), *v.Projectid)) + must(f.SetCellValue(containerSheetName, "C"+fmt.Sprint(i+2), *v.Projectname)) + must(f.SetCellValue(containerSheetName, "D"+fmt.Sprint(i+2), *v.Clusterid)) + must(f.SetCellValue(containerSheetName, "E"+fmt.Sprint(i+2), *v.Clustername)) + must(f.SetCellValue(containerSheetName, "F"+fmt.Sprint(i+2), *v.Namespace)) + must(f.SetCellValue(containerSheetName, "G"+fmt.Sprint(i+2), *v.Poduuid)) + must(f.SetCellValue(containerSheetName, "H"+fmt.Sprint(i+2), *v.Podname)) + must(f.SetCellValue(containerSheetName, "I"+fmt.Sprint(i+2), *v.Containername)) + must(f.SetCellValue(containerSheetName, "J"+fmt.Sprint(i+2), *v.Containerimage)) start := time.Time(*v.Podstart) - f.SetCellValue(containerSheetName, "K"+fmt.Sprint(i+2), start) - f.SetCellValue(containerSheetName, "L"+fmt.Sprint(i+2), *v.Lifetime/1000000000) + must(f.SetCellValue(containerSheetName, "K"+fmt.Sprint(i+2), start)) + must(f.SetCellValue(containerSheetName, "L"+fmt.Sprint(i+2), *v.Lifetime/1000000000)) cpuseconds, _ := strconv.Atoi(*v.Cpuseconds) - f.SetCellValue(containerSheetName, "M"+fmt.Sprint(i+2), cpuseconds) + must(f.SetCellValue(containerSheetName, "M"+fmt.Sprint(i+2), cpuseconds)) memoryseconds, _ := strconv.Atoi(*v.Memoryseconds) - f.SetCellValue(containerSheetName, "N"+fmt.Sprint(i+2), memoryseconds/1024/1024/1024) - f.SetCellValue(containerSheetName, "O"+fmt.Sprint(i+2), strings.Join(v.Annotations, "; ")) + must(f.SetCellValue(containerSheetName, "N"+fmt.Sprint(i+2), memoryseconds/1024/1024/1024)) + must(f.SetCellValue(containerSheetName, "O"+fmt.Sprint(i+2), strings.Join(v.Annotations, "; "))) } // Volume Billing @@ -560,41 +560,41 @@ func (c *config) excel() error { if err != nil { return err } - f.SetCellValue(volumeSheetName, "A1", "Tenant") - f.SetCellValue(volumeSheetName, "B1", "Project ID") - f.SetColWidth(volumeSheetName, "B", "B", 0) - f.SetCellValue(volumeSheetName, "C1", "Project") - f.SetCellValue(volumeSheetName, "D1", "Cluster ID") - f.SetColWidth(volumeSheetName, "D", "D", 0) - f.SetCellValue(volumeSheetName, "E1", "Cluster") - f.SetCellValue(volumeSheetName, "F1", "Volume ID") - f.SetColWidth(volumeSheetName, "F", "F", 0) - f.SetCellValue(volumeSheetName, "G1", "Volume") - f.SetCellValue(volumeSheetName, "H1", "Class") - f.SetCellValue(volumeSheetName, "I1", "Type") - f.SetCellValue(volumeSheetName, "J1", "Start") - f.SetColWidth(volumeSheetName, "J", "J", 15) - f.SetCellValue(volumeSheetName, "K1", "Lifetime[s]") - f.SetCellValue(volumeSheetName, "L1", "Capacity[GiB*s]") - f.SetCellValue(volumeSheetName, "M1", "Local[GiB*s]") - f.SetCellValue(volumeSheetName, "N1", "Block[GiB*s]") + must(f.SetCellValue(volumeSheetName, "A1", "Tenant")) + must(f.SetCellValue(volumeSheetName, "B1", "Project ID")) + must(f.SetColWidth(volumeSheetName, "B", "B", 0)) + must(f.SetCellValue(volumeSheetName, "C1", "Project")) + must(f.SetCellValue(volumeSheetName, "D1", "Cluster ID")) + must(f.SetColWidth(volumeSheetName, "D", "D", 0)) + must(f.SetCellValue(volumeSheetName, "E1", "Cluster")) + must(f.SetCellValue(volumeSheetName, "F1", "Volume ID")) + must(f.SetColWidth(volumeSheetName, "F", "F", 0)) + must(f.SetCellValue(volumeSheetName, "G1", "Volume")) + must(f.SetCellValue(volumeSheetName, "H1", "Class")) + must(f.SetCellValue(volumeSheetName, "I1", "Type")) + must(f.SetCellValue(volumeSheetName, "J1", "Start")) + must(f.SetColWidth(volumeSheetName, "J", "J", 15)) + must(f.SetCellValue(volumeSheetName, "K1", "Lifetime[s]")) + must(f.SetCellValue(volumeSheetName, "L1", "Capacity[GiB*s]")) + must(f.SetCellValue(volumeSheetName, "M1", "Local[GiB*s]")) + must(f.SetCellValue(volumeSheetName, "N1", "Block[GiB*s]")) for i, v := range responseVolume.Payload.Usage { - f.SetCellValue(volumeSheetName, "A"+fmt.Sprint(i+2), *v.Tenant) - f.SetCellValue(volumeSheetName, "B"+fmt.Sprint(i+2), *v.Projectid) - f.SetCellValue(volumeSheetName, "C"+fmt.Sprint(i+2), *v.Projectname) - f.SetCellValue(volumeSheetName, "D"+fmt.Sprint(i+2), *v.Clusterid) - f.SetCellValue(volumeSheetName, "E"+fmt.Sprint(i+2), *v.Clustername) - f.SetCellValue(volumeSheetName, "F"+fmt.Sprint(i+2), *v.UUID) - f.SetCellValue(volumeSheetName, "G"+fmt.Sprint(i+2), *v.Name) - f.SetCellValue(volumeSheetName, "H"+fmt.Sprint(i+2), *v.Class) - f.SetCellValue(volumeSheetName, "I"+fmt.Sprint(i+2), *v.Type) + must(f.SetCellValue(volumeSheetName, "A"+fmt.Sprint(i+2), *v.Tenant)) + must(f.SetCellValue(volumeSheetName, "B"+fmt.Sprint(i+2), *v.Projectid)) + must(f.SetCellValue(volumeSheetName, "C"+fmt.Sprint(i+2), *v.Projectname)) + must(f.SetCellValue(volumeSheetName, "D"+fmt.Sprint(i+2), *v.Clusterid)) + must(f.SetCellValue(volumeSheetName, "E"+fmt.Sprint(i+2), *v.Clustername)) + must(f.SetCellValue(volumeSheetName, "F"+fmt.Sprint(i+2), *v.UUID)) + must(f.SetCellValue(volumeSheetName, "G"+fmt.Sprint(i+2), *v.Name)) + must(f.SetCellValue(volumeSheetName, "H"+fmt.Sprint(i+2), *v.Class)) + must(f.SetCellValue(volumeSheetName, "I"+fmt.Sprint(i+2), *v.Type)) start := time.Time(*v.Start) - f.SetCellValue(volumeSheetName, "J"+fmt.Sprint(i+2), start) - f.SetCellValue(volumeSheetName, "K"+fmt.Sprint(i+2), *v.Lifetime/1000000000) + must(f.SetCellValue(volumeSheetName, "J"+fmt.Sprint(i+2), start)) + must(f.SetCellValue(volumeSheetName, "K"+fmt.Sprint(i+2), *v.Lifetime/1000000000)) capacityseconds, _ := strconv.Atoi(*v.Capacityseconds) - f.SetCellValue(volumeSheetName, "L"+fmt.Sprint(i+2), capacityseconds/1024/1024/1024) - f.SetCellFormula(volumeSheetName, "M"+fmt.Sprint(i+2), "=IF(LEFT(H"+fmt.Sprint(i+2)+",7)=\"csi-lvm\",L"+fmt.Sprint(i+2)+",0)") - f.SetCellFormula(volumeSheetName, "N"+fmt.Sprint(i+2), "=IF(LEFT(H"+fmt.Sprint(i+2)+",10)=\"partition-\",L"+fmt.Sprint(i+2)+",0)") + must(f.SetCellValue(volumeSheetName, "L"+fmt.Sprint(i+2), capacityseconds/1024/1024/1024)) + must(f.SetCellFormula(volumeSheetName, "M"+fmt.Sprint(i+2), "=IF(LEFT(H"+fmt.Sprint(i+2)+",7)=\"csi-lvm\",L"+fmt.Sprint(i+2)+",0)")) + must(f.SetCellFormula(volumeSheetName, "N"+fmt.Sprint(i+2), "=IF(LEFT(H"+fmt.Sprint(i+2)+",10)=\"partition-\",L"+fmt.Sprint(i+2)+",0)")) } // IP Billing @@ -616,19 +616,19 @@ func (c *config) excel() error { if err != nil { return err } - f.SetCellValue(ipSheetName, "A1", "Tenant") - f.SetCellValue(ipSheetName, "B1", "Project ID") - f.SetColWidth(ipSheetName, "B", "B", 0) - f.SetCellValue(ipSheetName, "C1", "Project") - f.SetCellValue(ipSheetName, "D1", "IP") - f.SetColWidth(ipSheetName, "D", "D", 14) - f.SetCellValue(ipSheetName, "E1", "Lifetime[s]") + must(f.SetCellValue(ipSheetName, "A1", "Tenant")) + must(f.SetCellValue(ipSheetName, "B1", "Project ID")) + must(f.SetColWidth(ipSheetName, "B", "B", 0)) + must(f.SetCellValue(ipSheetName, "C1", "Project")) + must(f.SetCellValue(ipSheetName, "D1", "IP")) + must(f.SetColWidth(ipSheetName, "D", "D", 14)) + must(f.SetCellValue(ipSheetName, "E1", "Lifetime[s]")) for i, v := range responseIP.Payload.Usage { - f.SetCellValue(ipSheetName, "A"+fmt.Sprint(i+2), *v.Tenant) - f.SetCellValue(ipSheetName, "B"+fmt.Sprint(i+2), *v.Projectid) - f.SetCellValue(ipSheetName, "C"+fmt.Sprint(i+2), *v.Projectname) - f.SetCellValue(ipSheetName, "D"+fmt.Sprint(i+2), *v.IP) - f.SetCellValue(ipSheetName, "E"+fmt.Sprint(i+2), *v.Lifetime/1000000000) + must(f.SetCellValue(ipSheetName, "A"+fmt.Sprint(i+2), *v.Tenant)) + must(f.SetCellValue(ipSheetName, "B"+fmt.Sprint(i+2), *v.Projectid)) + must(f.SetCellValue(ipSheetName, "C"+fmt.Sprint(i+2), *v.Projectname)) + must(f.SetCellValue(ipSheetName, "D"+fmt.Sprint(i+2), *v.IP)) + must(f.SetCellValue(ipSheetName, "E"+fmt.Sprint(i+2), *v.Lifetime/1000000000)) } // network-traffic Billing @@ -650,30 +650,30 @@ func (c *config) excel() error { if err != nil { return err } - f.SetCellValue(networkSheetName, "A1", "Tenant") - f.SetCellValue(networkSheetName, "B1", "Project ID") - f.SetColWidth(networkSheetName, "B", "B", 0) - f.SetCellValue(networkSheetName, "C1", "Project") - f.SetCellValue(networkSheetName, "D1", "Cluster ID") - f.SetColWidth(networkSheetName, "D", "D", 0) - f.SetCellValue(networkSheetName, "E1", "Cluster") - f.SetCellValue(networkSheetName, "F1", "Device") - f.SetCellValue(networkSheetName, "G1", "In[GiB]") - f.SetCellValue(networkSheetName, "H1", "Out[GiB]") - f.SetCellValue(networkSheetName, "I1", "Total[GiB]") + must(f.SetCellValue(networkSheetName, "A1", "Tenant")) + must(f.SetCellValue(networkSheetName, "B1", "Project ID")) + must(f.SetColWidth(networkSheetName, "B", "B", 0)) + must(f.SetCellValue(networkSheetName, "C1", "Project")) + must(f.SetCellValue(networkSheetName, "D1", "Cluster ID")) + must(f.SetColWidth(networkSheetName, "D", "D", 0)) + must(f.SetCellValue(networkSheetName, "E1", "Cluster")) + must(f.SetCellValue(networkSheetName, "F1", "Device")) + must(f.SetCellValue(networkSheetName, "G1", "In[GiB]")) + must(f.SetCellValue(networkSheetName, "H1", "Out[GiB]")) + must(f.SetCellValue(networkSheetName, "I1", "Total[GiB]")) for i, v := range responseNetwork.Payload.Usage { - f.SetCellValue(networkSheetName, "A"+fmt.Sprint(i+2), *v.Tenant) - f.SetCellValue(networkSheetName, "B"+fmt.Sprint(i+2), *v.Projectid) - f.SetCellValue(networkSheetName, "C"+fmt.Sprint(i+2), *v.Projectname) - f.SetCellValue(networkSheetName, "D"+fmt.Sprint(i+2), *v.Clusterid) - f.SetCellValue(networkSheetName, "E"+fmt.Sprint(i+2), *v.Clustername) - f.SetCellValue(networkSheetName, "F"+fmt.Sprint(i+2), *v.Device) + must(f.SetCellValue(networkSheetName, "A"+fmt.Sprint(i+2), *v.Tenant)) + must(f.SetCellValue(networkSheetName, "B"+fmt.Sprint(i+2), *v.Projectid)) + must(f.SetCellValue(networkSheetName, "C"+fmt.Sprint(i+2), *v.Projectname)) + must(f.SetCellValue(networkSheetName, "D"+fmt.Sprint(i+2), *v.Clusterid)) + must(f.SetCellValue(networkSheetName, "E"+fmt.Sprint(i+2), *v.Clustername)) + must(f.SetCellValue(networkSheetName, "F"+fmt.Sprint(i+2), *v.Device)) in, _ := strconv.Atoi(*v.In) - f.SetCellValue(networkSheetName, "G"+fmt.Sprint(i+2), in/1024/1024/1024) + must(f.SetCellValue(networkSheetName, "G"+fmt.Sprint(i+2), in/1024/1024/1024)) out, _ := strconv.Atoi(*v.Out) - f.SetCellValue(networkSheetName, "H"+fmt.Sprint(i+2), out/1024/1024/1024) + must(f.SetCellValue(networkSheetName, "H"+fmt.Sprint(i+2), out/1024/1024/1024)) total, _ := strconv.Atoi(*v.Total) - f.SetCellValue(networkSheetName, "I"+fmt.Sprint(i+2), total/1024/1024/1024) + must(f.SetCellValue(networkSheetName, "I"+fmt.Sprint(i+2), total/1024/1024/1024)) } // S3 Billing @@ -695,31 +695,31 @@ func (c *config) excel() error { if err != nil { return err } - f.SetCellValue(s3SheetName, "A1", "Tenant") - f.SetCellValue(s3SheetName, "B1", "Project ID") - f.SetColWidth(s3SheetName, "B", "B", 0) - f.SetCellValue(s3SheetName, "C1", "Project") - f.SetCellValue(s3SheetName, "D1", "Partition") - f.SetCellValue(s3SheetName, "E1", "User") - f.SetCellValue(s3SheetName, "F1", "Bucket ID") - f.SetColWidth(s3SheetName, "F", "F", 0) - f.SetCellValue(s3SheetName, "G1", "Bucket") - f.SetCellValue(s3SheetName, "H1", "Objects") - f.SetCellValue(s3SheetName, "I1", "Capacity[GiB*s]") - f.SetCellValue(s3SheetName, "J1", "Lifetime[s]") + must(f.SetCellValue(s3SheetName, "A1", "Tenant")) + must(f.SetCellValue(s3SheetName, "B1", "Project ID")) + must(f.SetColWidth(s3SheetName, "B", "B", 0)) + must(f.SetCellValue(s3SheetName, "C1", "Project")) + must(f.SetCellValue(s3SheetName, "D1", "Partition")) + must(f.SetCellValue(s3SheetName, "E1", "User")) + must(f.SetCellValue(s3SheetName, "F1", "Bucket ID")) + must(f.SetColWidth(s3SheetName, "F", "F", 0)) + must(f.SetCellValue(s3SheetName, "G1", "Bucket")) + must(f.SetCellValue(s3SheetName, "H1", "Objects")) + must(f.SetCellValue(s3SheetName, "I1", "Capacity[GiB*s]")) + must(f.SetCellValue(s3SheetName, "J1", "Lifetime[s]")) for i, v := range responseS3.Payload.Usage { - f.SetCellValue(s3SheetName, "A"+fmt.Sprint(i+2), *v.Tenant) - f.SetCellValue(s3SheetName, "B"+fmt.Sprint(i+2), *v.Projectid) - f.SetCellValue(s3SheetName, "C"+fmt.Sprint(i+2), *v.Projectname) - f.SetCellValue(s3SheetName, "D"+fmt.Sprint(i+2), *v.Partition) - f.SetCellValue(s3SheetName, "E"+fmt.Sprint(i+2), *v.User) - f.SetCellValue(s3SheetName, "F"+fmt.Sprint(i+2), *v.Bucketid) - f.SetCellValue(s3SheetName, "G"+fmt.Sprint(i+2), *v.Bucketname) + must(f.SetCellValue(s3SheetName, "A"+fmt.Sprint(i+2), *v.Tenant)) + must(f.SetCellValue(s3SheetName, "B"+fmt.Sprint(i+2), *v.Projectid)) + must(f.SetCellValue(s3SheetName, "C"+fmt.Sprint(i+2), *v.Projectname)) + must(f.SetCellValue(s3SheetName, "D"+fmt.Sprint(i+2), *v.Partition)) + must(f.SetCellValue(s3SheetName, "E"+fmt.Sprint(i+2), *v.User)) + must(f.SetCellValue(s3SheetName, "F"+fmt.Sprint(i+2), *v.Bucketid)) + must(f.SetCellValue(s3SheetName, "G"+fmt.Sprint(i+2), *v.Bucketname)) objects, _ := strconv.Atoi(*v.Currentnumberofobjects) - f.SetCellValue(s3SheetName, "H"+fmt.Sprint(i+2), objects) + must(f.SetCellValue(s3SheetName, "H"+fmt.Sprint(i+2), objects)) capacityseconds, _ := strconv.Atoi(*v.Storageseconds) - f.SetCellValue(s3SheetName, "I"+fmt.Sprint(i+2), capacityseconds/1024/1024/1024) - f.SetCellValue(s3SheetName, "J"+fmt.Sprint(i+2), *v.Lifetime/1000000000) + must(f.SetCellValue(s3SheetName, "I"+fmt.Sprint(i+2), capacityseconds/1024/1024/1024)) + must(f.SetCellValue(s3SheetName, "J"+fmt.Sprint(i+2), *v.Lifetime/1000000000)) } // Postgres Billing @@ -741,34 +741,34 @@ func (c *config) excel() error { if err != nil { return err } - f.SetCellValue(postgresSheetName, "A1", "Tenant") - f.SetCellValue(postgresSheetName, "B1", "Project ID") - f.SetColWidth(postgresSheetName, "B", "B", 0) - f.SetCellValue(postgresSheetName, "C1", "Project") - f.SetCellValue(postgresSheetName, "D1", "Postgres ID") - f.SetColWidth(postgresSheetName, "D", "D", 0) - f.SetCellValue(postgresSheetName, "E1", "Description") - f.SetCellValue(postgresSheetName, "F1", "Start") - f.SetColWidth(postgresSheetName, "F", "F", 15) - f.SetCellValue(postgresSheetName, "G1", "CPU[CPU*s]") - f.SetCellValue(postgresSheetName, "H1", "Memory[GiB*s]") - f.SetCellValue(postgresSheetName, "I1", "Storage[GiB*s]") - f.SetCellValue(postgresSheetName, "J1", "Lifetime[s]") + must(f.SetCellValue(postgresSheetName, "A1", "Tenant")) + must(f.SetCellValue(postgresSheetName, "B1", "Project ID")) + must(f.SetColWidth(postgresSheetName, "B", "B", 0)) + must(f.SetCellValue(postgresSheetName, "C1", "Project")) + must(f.SetCellValue(postgresSheetName, "D1", "Postgres ID")) + must(f.SetColWidth(postgresSheetName, "D", "D", 0)) + must(f.SetCellValue(postgresSheetName, "E1", "Description")) + must(f.SetCellValue(postgresSheetName, "F1", "Start")) + must(f.SetColWidth(postgresSheetName, "F", "F", 15)) + must(f.SetCellValue(postgresSheetName, "G1", "CPU[CPU*s]")) + must(f.SetCellValue(postgresSheetName, "H1", "Memory[GiB*s]")) + must(f.SetCellValue(postgresSheetName, "I1", "Storage[GiB*s]")) + must(f.SetCellValue(postgresSheetName, "J1", "Lifetime[s]")) for i, v := range responsePostgres.Payload.Usage { - f.SetCellValue(postgresSheetName, "A"+fmt.Sprint(i+2), *v.Tenant) - f.SetCellValue(postgresSheetName, "B"+fmt.Sprint(i+2), *v.Projectid) - f.SetCellValue(postgresSheetName, "C"+fmt.Sprint(i+2), "") - f.SetCellValue(postgresSheetName, "D"+fmt.Sprint(i+2), *v.Postgresid) - f.SetCellValue(postgresSheetName, "E"+fmt.Sprint(i+2), *v.Postgresdescription) + must(f.SetCellValue(postgresSheetName, "A"+fmt.Sprint(i+2), *v.Tenant)) + must(f.SetCellValue(postgresSheetName, "B"+fmt.Sprint(i+2), *v.Projectid)) + must(f.SetCellValue(postgresSheetName, "C"+fmt.Sprint(i+2), "")) + must(f.SetCellValue(postgresSheetName, "D"+fmt.Sprint(i+2), *v.Postgresid)) + must(f.SetCellValue(postgresSheetName, "E"+fmt.Sprint(i+2), *v.Postgresdescription)) start := time.Time(*v.Postgresstart) - f.SetCellValue(postgresSheetName, "F"+fmt.Sprint(i+2), start) + must(f.SetCellValue(postgresSheetName, "F"+fmt.Sprint(i+2), start)) cpuseconds, _ := strconv.Atoi(*v.Cpuseconds) - f.SetCellValue(postgresSheetName, "G"+fmt.Sprint(i+2), cpuseconds) + must(f.SetCellValue(postgresSheetName, "G"+fmt.Sprint(i+2), cpuseconds)) memoryseconds, _ := strconv.Atoi(*v.Memoryseconds) - f.SetCellValue(postgresSheetName, "H"+fmt.Sprint(i+2), memoryseconds/1024/1024/1024) + must(f.SetCellValue(postgresSheetName, "H"+fmt.Sprint(i+2), memoryseconds/1024/1024/1024)) storageseconds, _ := strconv.Atoi(*v.Storageseconds) - f.SetCellValue(postgresSheetName, "I"+fmt.Sprint(i+2), storageseconds/1024/1024/1024) - f.SetCellValue(postgresSheetName, "J"+fmt.Sprint(i+2), *v.Lifetime/1000000000) + must(f.SetCellValue(postgresSheetName, "I"+fmt.Sprint(i+2), storageseconds/1024/1024/1024)) + must(f.SetCellValue(postgresSheetName, "J"+fmt.Sprint(i+2), *v.Lifetime/1000000000)) } filename := ""