cmd/jobsShow.go (133 lines of code) (raw):
// Copyright © 2017 Microsoft <wastore@microsoft.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package cmd
import (
"errors"
"fmt"
"strings"
"encoding/json"
"github.com/Azure/azure-storage-azcopy/v10/common"
"github.com/spf13/cobra"
)
type ListReq struct {
JobID common.JobID
OfStatus string
}
func init() {
commandLineInput := ListReq{}
// shJob represents the ls command
shJob := &cobra.Command{
Use: "show [jobID]",
Short: showJobsCmdShortDescription,
Long: showJobsCmdLongDescription,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return errors.New("show job command requires only the JobID")
}
// Parse the JobId
jobId, err := common.ParseJobID(args[0])
if err != nil {
return errors.New("invalid jobId given " + args[0])
}
commandLineInput.JobID = jobId
return nil
},
Run: func(cmd *cobra.Command, args []string) {
listRequest := common.ListRequest{}
listRequest.JobID = commandLineInput.JobID
listRequest.OfStatus = commandLineInput.OfStatus
err := HandleShowCommand(listRequest)
if err == nil {
glcm.Exit(nil, common.EExitCode.Success())
} else {
glcm.Error(err.Error())
}
},
}
jobsCmd.AddCommand(shJob)
// filters
shJob.PersistentFlags().StringVar(&commandLineInput.OfStatus, "with-status", "", "List only the transfers of job with the specified status. Available values include: All, Started, Success, Failed.")
}
// handles the list command
// dispatches the list order to the transfer engine
func HandleShowCommand(listRequest common.ListRequest) error {
if listRequest.OfStatus == "" {
resp := common.ListJobSummaryResponse{}
rpcCmd := common.ERpcCmd.ListJobSummary()
Rpc(rpcCmd, &listRequest.JobID, &resp)
PrintJobProgressSummary(resp)
} else {
lsRequest := common.ListJobTransfersRequest{}
lsRequest.JobID = listRequest.JobID
// Parse the given expected Transfer Status
// If there is an error parsing, then kill return the error
err := lsRequest.OfStatus.Parse(listRequest.OfStatus)
if err != nil {
return fmt.Errorf("cannot parse the given Transfer Status %s", listRequest.OfStatus)
}
resp := common.ListJobTransfersResponse{}
rpcCmd := common.ERpcCmd.ListJobTransfers()
Rpc(rpcCmd, lsRequest, &resp)
PrintJobTransfers(resp)
}
return nil
}
// PrintJobTransfers prints the response of listOrder command when list Order command requested the list of specific transfer of an existing job
func PrintJobTransfers(listTransfersResponse common.ListJobTransfersResponse) {
if listTransfersResponse.ErrorMsg != "" {
glcm.Error("request failed with following message " + listTransfersResponse.ErrorMsg)
}
glcm.Exit(func(format common.OutputFormat) string {
if format == common.EOutputFormat.Json() {
jsonOutput, err := json.Marshal(listTransfersResponse)
common.PanicIfErr(err)
return string(jsonOutput)
}
var sb strings.Builder
sb.WriteString("----------- Transfers for JobId " + listTransfersResponse.JobID.String() + " -----------\n")
for index := 0; index < len(listTransfersResponse.Details); index++ {
folderChar := ""
if listTransfersResponse.Details[index].IsFolderProperties {
folderChar = "/"
}
sb.WriteString("transfer--> source: " + listTransfersResponse.Details[index].Src + folderChar + " destination: " +
listTransfersResponse.Details[index].Dst + folderChar + " status " + listTransfersResponse.Details[index].TransferStatus.String() + "\n")
}
return sb.String()
}, common.EExitCode.Success())
}
// PrintJobProgressSummary prints the response of listOrder command when listOrder command requested the progress summary of an existing job
func PrintJobProgressSummary(summary common.ListJobSummaryResponse) {
if summary.ErrorMsg != "" {
glcm.Error("list progress summary of job failed because " + summary.ErrorMsg)
}
// Reset the bytes over the wire counter
summary.BytesOverWire = 0
glcm.Exit(func(format common.OutputFormat) string {
if format == common.EOutputFormat.Json() {
jsonOutput, err := json.Marshal(summary) // see note below re % complete being approximate. We can't include "approx" in the JSON.
common.PanicIfErr(err)
return string(jsonOutput)
}
return fmt.Sprintf(
`
Job %s summary
Number of File Transfers: %v
Number of Folder Property Transfers: %v
Number of Symlink Transfers: %v
Total Number of Transfers: %v
Number of File Transfers Completed: %v
Number of Folder Transfers Completed: %v
Number of File Transfers Failed: %v
Number of Folder Transfers Failed: %v
Number of File Transfers Skipped: %v
Number of Folder Transfers Skipped: %v
Total Number of Bytes Transferred: %v
Percent Complete (approx): %.1f
Final Job Status: %v
`,
summary.JobID.String(),
summary.FileTransfers,
summary.FolderPropertyTransfers,
summary.SymlinkTransfers,
summary.TotalTransfers,
summary.TransfersCompleted-summary.FoldersCompleted,
summary.FoldersCompleted,
summary.TransfersFailed-summary.FoldersFailed,
summary.FoldersFailed,
summary.TransfersSkipped-summary.FoldersSkipped,
summary.FoldersSkipped,
summary.TotalBytesTransferred,
summary.PercentComplete, // noted as approx in the format string because won't include in-flight files if this Show command is run from a different process
summary.JobStatus,
)
}, common.EExitCode.Success())
}