Skip to content

Commit

Permalink
Fixes to pelican plugin staging
Browse files Browse the repository at this point in the history
Changes:
- Found that how condor gives hooks a classad is in a different format
  than the plugin for some reason. For the hook the classad is split up
  by newlines but for the plugin it is by ';'. Therefore, made a new
  function for plugin stage to utilize.
- Added function within the plugin to force logging to go to stderr
  (even though that is the default) as well as a file called
  `.PelicanPlugin.log`. This extra file is something you can specify in
  a condor submit file in `transfer_output_files` to get it back but only works if the
  job does not go on hold/if the plugin does not fail (not super helpful
  yet)
  • Loading branch information
joereuss12 committed Mar 1, 2024
1 parent 9e7c0b7 commit 764da0e
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 1 deletion.
69 changes: 69 additions & 0 deletions classads/classads.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,72 @@ func attributeSplitFunc(data []byte, atEOF bool) (advance int, token []byte, err
}
return 0, nil, nil
}

func ParseShadowClassAd(line string) (ClassAd, error) {
var ad ClassAd
ad.attributes = make(map[string]interface{})

// Trim the spaces and "[" "]"
line = strings.TrimSpace(line)
line = strings.TrimPrefix(line, "[")
line = strings.TrimSuffix(line, "]")

attributeScanner := bufio.NewScanner(strings.NewReader(line))
attributeScanner.Split(attributeShadowSplitFunc)
for attributeScanner.Scan() {
attrStr := attributeScanner.Text()
attrStr = strings.TrimSpace(attrStr)
if attrStr == "" {
continue
}

// Split on the first "="
attrSplit := strings.SplitN(attrStr, "=", 2)
name := strings.TrimSpace(attrSplit[0])

// Check for quoted attribute and remove it
value := strings.TrimSpace(attrSplit[1])

// If the value is quoted, remove the quotes
if strings.HasPrefix(value, "\"") && strings.HasSuffix(value, "\"") {
value = strings.Trim(value, "\"")
}

// Convert the value based on its type
if intValue, err := strconv.Atoi(value); err == nil {
// If the value is a number, we know it's an integer
ad.Set(name, intValue)
} else if floatValue, err := strconv.ParseFloat(value, 64); err == nil {
// If the value is a float, we know it's a float
ad.Set(name, floatValue)
} else if value == "true" || value == "false" {
// If the value is a boolean, we know it's a boolean
ad.Set(name, value == "true")
} else {
// Otherwise, we assume it's a string
ad.Set(name, value)
}
}
return ad, nil
}

// Split the classad by attribute, at the first semi-colon not in quotes
func attributeShadowSplitFunc(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}

// Look for the next newline character in the input data
if i := strings.Index(string(data), "\n"); i >= 0 {
// Found a newline character, return the split point
return i + 1, data[0:i], nil
}

// If at end of file and no newline character is found, return the entire remaining data
if atEOF {
return len(data), data, nil
}

// Need more data to find a newline character
return 0, nil, nil
}
31 changes: 31 additions & 0 deletions cmd/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"github.com/pelicanplatform/pelican/config"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
"github.com/sirupsen/logrus/hooks/writer"
"github.com/spf13/cobra"
"golang.org/x/sync/errgroup"
)
Expand Down Expand Up @@ -115,6 +116,12 @@ func stashPluginMain(args []string) {
err = errors.Wrap(err, "Problem initializing the Pelican Client configuration")
isConfigErr = true
}
loggingErr := setPluginLogging()
if loggingErr != nil {
// Note: not exiting here because logs should have default output of os.Stderr which is what we mainly want anyway.
// Therefore, just warn the user and continue
log.Warningf("Problem setting logging for the pelican plugin: %v", loggingErr)
}

// Parse command line arguments
var upload bool = false
Expand Down Expand Up @@ -553,6 +560,30 @@ func writeOutfile(err error, resultAds []*classads.ClassAd, outputFile *os.File)
return success, retryable
}

func setPluginLogging() (err error) {
file, err := os.OpenFile(".PelicanPlugin.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
err = errors.New("Failed to open log file for writing.")
} else {
// Add file output to log
log.AddHook(&writer.Hook{
Writer: file,
LogLevels: []log.Level{
log.DebugLevel,
log.InfoLevel,
log.WarnLevel,
log.ErrorLevel,
log.FatalLevel,
log.PanicLevel,
},
})
}
// Default is stderr, but want to ensure it remains for the plugin
log.SetOutput(os.Stderr)

return err
}

// readMultiTransfers reads the transfers from a Reader, such as stdin
func readMultiTransfers(stdin bufio.Reader) (transfers []PluginTransfer, err error) {
// Check stdin for a list of transfers
Expand Down
3 changes: 2 additions & 1 deletion cmd/plugin_stage.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,14 +138,15 @@ func stagePluginMain(cmd *cobra.Command, args []string) {
log.Errorln("Failed to read ClassAd from stdin:", err)
os.Exit(1)
}
classad, err := classads.ParseClassAd(string(buffer[:bytesread]))
classad, err := classads.ParseShadowClassAd(string(buffer[:bytesread]))
if err != nil {
log.Errorln("Failed to parse ClassAd from stdin: ", err)
os.Exit(1)
}
inputList, err := classad.Get("TransferInput")
if err != nil || inputList == nil {
// No TransferInput, no need to transform...
log.Debugln("No transfer input found in classad, no need to transform.")
os.Exit(0)
}
inputListStr, ok := inputList.(string)
Expand Down

0 comments on commit 764da0e

Please sign in to comment.