github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/pkg/cmd/termination/termination.go (about) 1 package termination 2 3 import ( 4 "context" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "os" 9 "path" 10 11 log "github.com/authzed/spicedb/internal/logging" 12 "github.com/authzed/spicedb/pkg/spiceerrors" 13 14 "github.com/jzelinskie/cobrautil/v2" 15 "github.com/spf13/cobra" 16 flag "github.com/spf13/pflag" 17 ) 18 19 const ( 20 terminationLogFlagName = "termination-log-path" 21 kubeTerminationLogLimit = 4096 22 ) 23 24 // PublishError returns a new wrapping cobra run function that executes the provided argument runFunc, and 25 // writes to disk an error returned by the latter if it is of type termination.TerminationError. 26 func PublishError(runFunc cobrautil.CobraRunFunc) cobrautil.CobraRunFunc { 27 return func(cmd *cobra.Command, args []string) error { 28 runFuncErr := runFunc(cmd, args) 29 var termErr spiceerrors.TerminationError 30 if runFuncErr != nil && errors.As(runFuncErr, &termErr) { 31 ctx := context.Background() 32 if cmd.Context() != nil { 33 ctx = cmd.Context() 34 } 35 terminationLogPath := cobrautil.MustGetString(cmd, terminationLogFlagName) 36 if terminationLogPath == "" { 37 return runFuncErr 38 } 39 bytes, err := json.Marshal(termErr) 40 if err != nil { 41 log.Ctx(ctx).Error().Err(fmt.Errorf("unable to marshall termination log: %w", err)).Msg("failed to report termination log") 42 return runFuncErr 43 } 44 45 if len(bytes) > kubeTerminationLogLimit { 46 log.Ctx(ctx).Warn().Msg("termination log exceeds 4096 bytes limit, metadata will be truncated") 47 termErr.Metadata = nil 48 bytes, err = json.Marshal(termErr) 49 if err != nil { 50 return runFuncErr 51 } 52 } 53 54 if _, err := os.Stat(path.Dir(terminationLogPath)); os.IsNotExist(err) { 55 mkdirErr := os.MkdirAll(path.Dir(terminationLogPath), 0o700) // Create your file 56 if mkdirErr != nil { 57 log.Ctx(ctx).Error().Err(fmt.Errorf("unable to create directory for termination log: %w", err)).Msg("failed to report termination log") 58 return runFuncErr 59 } 60 } 61 if err := os.WriteFile(terminationLogPath, bytes, 0o600); err != nil { 62 log.Ctx(ctx).Error().Err(fmt.Errorf("unable to write terminationlog file: %w", err)).Msg("failed to report termination log") 63 } 64 } 65 return runFuncErr 66 } 67 } 68 69 // RegisterFlags registers the termination log flag 70 func RegisterFlags(flagset *flag.FlagSet) { 71 flagset.String(terminationLogFlagName, 72 "", 73 "define the path to the termination log file, which contains a JSON payload to surface as reason for termination - disabled by default", 74 ) 75 }