package object import ( "encoding/hex" "errors" "fmt" "os" "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs" internalclient "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/client" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/common" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/commonflags" "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/frostfs-cli/internal/key" commonCmd "git.frostfs.info/TrueCloudLab/frostfs-node/cmd/internal/common" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" "github.com/spf13/cobra" ) var objectHeadCmd = &cobra.Command{ Use: "head", Short: "Get object header", Long: "Get object header", Run: getObjectHeader, } func initObjectHeadCmd() { commonflags.Init(objectHeadCmd) initFlagSession(objectHeadCmd, "HEAD") flags := objectHeadCmd.Flags() flags.String(commonflags.CIDFlag, "", commonflags.CIDFlagUsage) _ = objectHeadCmd.MarkFlagRequired(commonflags.CIDFlag) flags.String(commonflags.OIDFlag, "", commonflags.OIDFlagUsage) _ = objectHeadCmd.MarkFlagRequired(commonflags.OIDFlag) flags.String(fileFlag, "", "File to write header to. Default: stdout.") flags.Bool("main-only", false, "Return only main fields") flags.Bool(commonflags.JSON, false, "Marshal output in JSON") flags.Bool("proto", false, "Marshal output in Protobuf") flags.Bool(rawFlag, false, rawFlagDesc) } func getObjectHeader(cmd *cobra.Command, _ []string) { var cnr cid.ID var obj oid.ID objAddr := readObjectAddress(cmd, &cnr, &obj) mainOnly, _ := cmd.Flags().GetBool("main-only") pk := key.GetOrGenerate(cmd) cli := internalclient.GetSDKClientByFlag(cmd, pk, commonflags.RPC) var prm internalclient.HeadObjectPrm prm.SetClient(cli) Prepare(cmd, &prm) readSession(cmd, &prm, pk, cnr, obj) raw, _ := cmd.Flags().GetBool(rawFlag) prm.SetRawFlag(raw) prm.SetAddress(objAddr) prm.SetMainOnlyFlag(mainOnly) res, err := internalclient.HeadObject(cmd.Context(), prm) if err != nil { if ok := printSplitInfoErr(cmd, err); ok { return } if ok := printECInfoErr(cmd, err); ok { return } commonCmd.ExitOnErr(cmd, "rpc error: %w", err) } err = saveAndPrintHeader(cmd, res.Header(), cmd.Flag(fileFlag).Value.String()) commonCmd.ExitOnErr(cmd, "", err) } func saveAndPrintHeader(cmd *cobra.Command, obj *objectSDK.Object, filename string) error { bs, err := marshalHeader(cmd, obj) if err != nil { return fmt.Errorf("could not marshal header: %w", err) } if len(bs) != 0 { if filename == "" { cmd.Println(string(bs)) return nil } err = os.WriteFile(filename, bs, os.ModePerm) if err != nil { return fmt.Errorf("could not write header to file: %w", err) } cmd.Printf("[%s] Header successfully saved.", filename) } return printHeader(cmd, obj) } func marshalHeader(cmd *cobra.Command, hdr *objectSDK.Object) ([]byte, error) { toJSON, _ := cmd.Flags().GetBool(commonflags.JSON) toProto, _ := cmd.Flags().GetBool("proto") switch { case toJSON && toProto: return nil, errors.New("'--json' and '--proto' flags are mutually exclusive") case toJSON: return hdr.MarshalJSON() case toProto: return hdr.Marshal() default: return nil, nil } } func printObjectID(cmd *cobra.Command, recv func() (oid.ID, bool)) { var strID string id, ok := recv() if ok { strID = id.String() } else { strID = "" } cmd.Printf("ID: %s\n", strID) } func printContainerID(cmd *cobra.Command, recv func() (cid.ID, bool)) { var strID string id, ok := recv() if ok { strID = id.String() } else { strID = "" } cmd.Printf("CID: %s\n", strID) } func printHeader(cmd *cobra.Command, obj *objectSDK.Object) error { printObjectID(cmd, obj.ID) printContainerID(cmd, obj.ContainerID) cmd.Printf("Owner: %s\n", obj.OwnerID()) cmd.Printf("CreatedAt: %d\n", obj.CreationEpoch()) cmd.Printf("Size: %d\n", obj.PayloadSize()) common.PrintChecksum(cmd, "HomoHash", obj.PayloadHomomorphicHash) common.PrintChecksum(cmd, "Checksum", obj.PayloadChecksum) cmd.Printf("Type: %s\n", obj.Type()) cmd.Println("Attributes:") for _, attr := range obj.Attributes() { if attr.Key() == objectSDK.AttributeTimestamp { cmd.Printf(" %s=%s (%s)\n", attr.Key(), attr.Value(), common.PrettyPrintUnixTime(attr.Value())) continue } cmd.Printf(" %s=%s\n", attr.Key(), attr.Value()) } if signature := obj.Signature(); signature != nil { cmd.Print("ID signature:\n") // TODO(@carpawell): #468 implement and use another approach to avoid conversion var sigV2 refs.Signature signature.WriteToV2(&sigV2) cmd.Printf(" public key: %s\n", hex.EncodeToString(sigV2.GetKey())) cmd.Printf(" signature: %s\n", hex.EncodeToString(sigV2.GetSign())) } if ecHeader := obj.ECHeader(); ecHeader != nil { cmd.Print("EC header:\n") cmd.Printf(" parent object ID: %s\n", ecHeader.Parent().EncodeToString()) cmd.Printf(" index: %d\n", ecHeader.Index()) cmd.Printf(" total: %d\n", ecHeader.Total()) cmd.Printf(" header length: %d\n", ecHeader.HeaderLength()) } return printSplitHeader(cmd, obj) } func printSplitHeader(cmd *cobra.Command, obj *objectSDK.Object) error { if splitID := obj.SplitID(); splitID != nil { cmd.Printf("Split ID: %s\n", splitID) } if oid, ok := obj.ParentID(); ok { cmd.Printf("Split ParentID: %s\n", oid) } if prev, ok := obj.PreviousID(); ok { cmd.Printf("Split PreviousID: %s\n", prev) } for _, child := range obj.Children() { cmd.Printf("Split ChildID: %s\n", child.String()) } parent := obj.Parent() if parent != nil { cmd.Print("\nSplit Parent Header:\n") return printHeader(cmd, parent) } return nil }