Skip to content

Commit

Permalink
minor fixes in error messaging, added comments, removed server port
Browse files Browse the repository at this point in the history
  • Loading branch information
hsingh1 authored and hsingh1 committed Feb 16, 2024
1 parent a753eaf commit 1c12c2e
Show file tree
Hide file tree
Showing 19 changed files with 214 additions and 38 deletions.
3 changes: 1 addition & 2 deletions ccli_config.DEFAULT.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
##
## catalog address
server_addr: "http://localhost/api/graphql"
server_port: 80
##
## logging
## there are 2 log levels:
## 1 - debugger
## 2 - debugger with source data
log_file: 'log.txt'
log_level: 2
log_level: 1
##
##
## How much to indent the json format of an output file.
Expand Down
1 change: 0 additions & 1 deletion ccli_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
##
## catalog address
server_addr: "http://localhost/api/graphql"
server_port: 80
##
## logging
## there are 2 log levels:
Expand Down
21 changes: 10 additions & 11 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ package main

import (
_ "embed"
"fmt"
"log/slog"
"os"

Expand All @@ -34,32 +35,30 @@ func init() {
// set the config file and its path
viper.SetConfigFile("ccli_config.yml")
viper.AddConfigPath(".")
// create a default slog logger which logs to stdout
slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{})))
// read the config file
if err := viper.ReadInConfig(); err != nil {
if err != nil {
if err != errors.New("open ccli_config.yml: no such file or directory") {
slog.Error("User configuration file not found. Please create ccli_config.yml and copy the contents of ccli_config.DEFAULT.yml.")
fmt.Println("User configuration file not found. Please create ccli_config.yml and copy the contents of ccli_config.DEFAULT.yml.")
} else {
slog.Error("Error reading in config file", slog.Any("error", err))
fmt.Println("Error reading in config file. Error:", err)
}
os.Exit(1)
}
}
// unmarshal the config file parameters to a struct
if err := viper.Unmarshal(&configFile); err != nil {
slog.Error("Could not unmarshal config file parameters")
fmt.Println("Could not unmarshal config file parameters")
os.Exit(1)
}
// check if the log file is present and has the correct extension
if configFile.LogFile == "" || configFile.LogFile[len(configFile.LogFile)-4:] != ".txt" {
slog.Error("*** ERROR - Error reading config file, log file must be a .txt file")
fmt.Println("*** ERROR - Error reading config file, log file must be a .txt file")
os.Exit(1)
}
// check if the log level is accurate
if configFile.LogLevel > 2 || configFile.LogLevel < 1 {
slog.Error("*** ERROR - Error reading log level, log level must be either 1 or 2")
fmt.Println("*** ERROR - Error reading log level, log level must be either 1 or 2")
os.Exit(1)
}
indentString := ""
Expand All @@ -72,25 +71,25 @@ func init() {
func main() {
// check if the server address is provided
if configFile.ServerAddr == "" {
slog.Error("invalid configuration file, no server address located")
fmt.Println("invalid configuration file, no server address located")
os.Exit(1)
}
// contact the given server
resp, err := http.DefaultClient.Get(configFile.ServerAddr)
if err != nil {
slog.Error("error contacting server", slog.Any("error:", err))
fmt.Println("error contacting server", slog.Any("error:", err))
os.Exit(1)
}
resp.Body.Close()
// check if the response suggets a successful connection to the server
if resp.StatusCode != 200 && resp.StatusCode != 422 {
slog.Error("server connection error, check config file and network configuration", slog.Int("Status Code:", resp.StatusCode))
fmt.Println("server connection error, check config file and network configuration", slog.Int("Status Code:", resp.StatusCode))
os.Exit(1)
}
// create the log file or truncate it if already present
logFile, err := os.Create(configFile.LogFile)
if err != nil {
slog.Error("*** ERROR - Error opening log file:", slog.Any("error", err))
fmt.Println("*** ERROR - Error opening log file. Error:", err)
os.Exit(1)
}
// create a new log writer for writing to the log file and stdout simultaneously
Expand Down
42 changes: 40 additions & 2 deletions packages/cmd/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,16 @@ import (
// Add() handles uploading a logical part or a part profile
// to the catalog from a yml file
func Add(configFile *config.ConfigData, client *graph.Client, indent string) *cobra.Command {
// create a cobra command
addCmd := &cobra.Command{
Use: "add",
Short: "Add a specific component(part or profile) to Software Parts Catalog.",
// the function to be executed when the command is ran
RunE: func(cmd *cobra.Command, args []string) error {
return errors.New("Please provide the component type to be added(i.e. part or profile). For more info run help")
},
}
// attach sub commands
addCmd.AddCommand(AddPart(configFile, client, indent))
addCmd.AddCommand(AddProfile(configFile, client, indent))
return addCmd
Expand All @@ -50,37 +53,46 @@ func AddPart(configFile *config.ConfigData, client *graph.Client, indent string)
addPartCmd := &cobra.Command{
Use: "part [path]",
Short: "Add a part to Software Parts Catalog.",
// the function to be executed as a setup to the command being ran
PreRunE: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("No path provided.\nUsage: ccli add part <path to file>")
return errors.New("No path provided.")
}
return nil
},
// the function be executed when the command is ran
RunE: func(cmd *cobra.Command, args []string) error {
// scan the arguments to the command
argPartImportPath := args[0]
if argPartImportPath != "" {
// check if the file is of yaml/yml format
if argPartImportPath[len(argPartImportPath)-5:] != ".yaml" && argPartImportPath[len(argPartImportPath)-4:] != ".yml" {
return errors.New("error importing part, import path not a yaml file")
}
// open the file
f, err := os.Open(argPartImportPath)
if err != nil {
return errors.Wrap(err, "error opening file")
}
defer f.Close()
slog.Debug("successfully opened file", slog.String("file:", argPartImportPath))
// read the contents of the file
data, err := io.ReadAll(f)
if err != nil {
return errors.Wrapf(err, "error reading file")
}
var partData yaml.Part
// unmarshal all the data of the file into a struct
if err = yaml.Unmarshal(data, &partData); err != nil {
return errors.Wrapf(err, "error unmarshaling file contents")
}
slog.Debug("adding part")
// call the graphql helper for adding a new part
createdPart, err := graphql.AddPart(context.Background(), client, partData)
if err != nil {
return errors.Wrapf(err, "error adding part")
}
// create a indented json output for the path added
prettyPart, err := json.MarshalIndent(&createdPart, "", indent)
if err != nil {
return errors.Wrapf(err, "error prettifying json")
Expand All @@ -98,55 +110,68 @@ func AddPart(configFile *config.ConfigData, client *graph.Client, indent string)
// AddProfile() handles the upload of a part's profile
// like license, security and quality using a yml file
func AddProfile(configFile *config.ConfigData, client *graph.Client, indent string) *cobra.Command {
// create a cobra command
addProfileCmd := &cobra.Command{
Use: "profile [path]",
Short: "Add a profile to a part in Software Parts Catalog.",
// function to be run as a setup for the command
PreRunE: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("No path provided.\nUsage: ccli add profile <path to file>")
return errors.New("No path provided.")
}
return nil
},
// function to be run on command execution
RunE: func(cmd *cobra.Command, args []string) error {
argImportPath := args[0]
if argImportPath != "" {
// check if the file is of yaml/yml format
if argImportPath[len(argImportPath)-5:] != ".yaml" && argImportPath[len(argImportPath)-4:] != ".yml" {
return errors.New("error importing profile, import path not a yaml file")
}
// open the file
f, err := os.Open(argImportPath)
if err != nil {
return errors.Wrapf(err, "error opening file")
}
defer f.Close()
slog.Debug("Successfully opened file", slog.String("file:", argImportPath))
// read the file data
data, err := io.ReadAll(f)
if err != nil {
return errors.Wrapf(err, "error reading file")
}
var profileData yaml.Profile
// unmarshal the file data into a struct
if err = yaml.Unmarshal(data, &profileData); err != nil {
return errors.Wrapf(err, "error unmarshaling file contents")
}
slog.Debug("adding profile", slog.String("Key", profileData.Profile))
// switch case for various profile types
switch profileData.Profile {
case "security":
var securityProfile yaml.SecurityProfile
// unmarshal the data into a security profile struct
if err = yaml.Unmarshal(data, &securityProfile); err != nil {
return errors.Wrapf(err, "error unmarshaling security profile")
}
// marshal the security profile struct into a json
jsonSecurityProfile, err := json.Marshal(securityProfile)
if err != nil {
return errors.Wrapf(err, "error marshaling json")
}
// check if the part identifier is present
if profileData.CatalogID == "" && profileData.FVC == "" && profileData.Sha256 == "" {
return errors.New("error adding profile, no part identifier given")
}
// add the profile if the part id is given
if profileData.CatalogID != "" {
if err = graphql.AddProfile(context.Background(), client, profileData.CatalogID, profileData.Profile, jsonSecurityProfile); err != nil {
return errors.Wrapf(err, "error adding profile")
}
fmt.Printf("Successfully added security profile to %s-%s\n", profileData.Name, profileData.Version)
}
// add the profile by first getting the part id using the fvc
if profileData.FVC != "" {
slog.Debug("retrieving part id by file verification code", slog.String("File Verification Code", profileData.FVC))
uuid, err := graphql.GetPartIDByFVC(context.Background(), client, profileData.FVC)
Expand All @@ -159,6 +184,7 @@ func AddProfile(configFile *config.ConfigData, client *graph.Client, indent stri
fmt.Printf("Successfully added security profile to %s-%s\n", profileData.Name, profileData.Version)
break
}
// add the profile by first getting the part id using the sha256
if profileData.Sha256 != "" {
slog.Debug("retrieving part id by sha256", slog.String("SHA256", profileData.Sha256))
uuid, err := graphql.GetPartIDBySha256(context.Background(), client, profileData.Sha256)
Expand All @@ -172,22 +198,27 @@ func AddProfile(configFile *config.ConfigData, client *graph.Client, indent stri
}
case "licensing":
var licensingProfile yaml.LicensingProfile
// unmarshal the data into a licensing profile struct
if err = yaml.Unmarshal(data, &licensingProfile); err != nil {
return errors.Wrapf(err, "error unmarshaling licensing profile")
}
// marshal the licensing profile struct to json
jsonLicensingProfile, err := json.Marshal(licensingProfile)
if err != nil {
return errors.Wrapf(err, "error marshaling json")
}
// check if the part identifier is given
if profileData.CatalogID == "" && profileData.FVC == "" && profileData.Sha256 == "" {
return errors.New("error adding profile, no part identifier given")
}
// add the profile using the part id
if profileData.CatalogID != "" {
if err = graphql.AddProfile(context.Background(), client, profileData.CatalogID, profileData.Profile, jsonLicensingProfile); err != nil {
return errors.Wrapf(err, "error adding profile")
}
fmt.Printf("Successfully added licensing profile to %s-%s\n", profileData.Name, profileData.Version)
}
// add the profile by first getting the part id using the fvc
if profileData.FVC != "" {
slog.Debug("retrieving part id by file verification code", slog.String("File Verification Code", profileData.FVC))
uuid, err := graphql.GetPartIDByFVC(context.Background(), client, profileData.FVC)
Expand All @@ -200,6 +231,7 @@ func AddProfile(configFile *config.ConfigData, client *graph.Client, indent stri
fmt.Printf("Successfully added licensing profile to %s-%s\n", profileData.Name, profileData.Version)
break
}
// add the profile by first getting the part id using the sha256
if profileData.Sha256 != "" {
slog.Debug("retrieving part id by sha256", slog.String("SHA256", profileData.Sha256))
uuid, err := graphql.GetPartIDBySha256(context.Background(), client, profileData.Sha256)
Expand All @@ -213,22 +245,27 @@ func AddProfile(configFile *config.ConfigData, client *graph.Client, indent stri
}
case "quality":
var qualityProfile yaml.QualityProfile
// unmarshal the data into a quality profile struct
if err = yaml.Unmarshal(data, &qualityProfile); err != nil {
return errors.Wrapf(err, "error unmarshaling quality profile")
}
// marshal the quality profile struct to a json
jsonQualityProfile, err := json.Marshal(qualityProfile)
if err != nil {
return errors.Wrapf(err, "error marshaling json")
}
// cehck if the part identifier is given
if profileData.CatalogID == "" && profileData.FVC == "" && profileData.Sha256 == "" {
return errors.Wrapf(err, "error adding profile, no part identifier given")
}
// add the profile by using the part id
if profileData.CatalogID != "" {
if err = graphql.AddProfile(context.Background(), client, profileData.CatalogID, profileData.Profile, jsonQualityProfile); err != nil {
return errors.Wrapf(err, "error adding profile")
}
fmt.Printf("Successfully added quality profile to %s-%s\n", profileData.Name, profileData.Version)
}
// add the profile by first getting the part id using the fvc
if profileData.FVC != "" {
slog.Debug("retrieving part id by file verification code", slog.String("File Verification Code", profileData.FVC))
uuid, err := graphql.GetPartIDByFVC(context.Background(), client, profileData.FVC)
Expand All @@ -241,6 +278,7 @@ func AddProfile(configFile *config.ConfigData, client *graph.Client, indent stri
fmt.Printf("Successfully added quality profile to %s-%s\n", profileData.Name, profileData.Version)
break
}
// add the profile by first getting the part id using the sha256
if profileData.Sha256 != "" {
slog.Debug("retrieving part id by sha256", slog.String("SHA256", profileData.Sha256))
uuid, err := graphql.GetPartIDBySha256(context.Background(), client, profileData.Sha256)
Expand Down
8 changes: 7 additions & 1 deletion packages/cmd/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,25 @@ import (
func Delete(configFile *config.ConfigData, client *graph.Client, indent string) *cobra.Command {
var argForcedMode bool
var argRecursiveMode bool
// cobra command for delete
deleteCmd := &cobra.Command{
Use: "delete [part id]",
Short: "Delete a part in the Software Parts Catalog.",
// function to run as a setup on command execution
PreRunE: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("No part id provided.\nUsage: ccli delete <part id>")
return errors.New("No part id provided.")
}
return nil
},
// function to run on command execution
RunE: func(cmd *cobra.Command, args []string) error {
// check if the part id is provided as an argument
argPartID := args[0]
if argPartID == "" {
return errors.New("error deleting part, delete subcommand usage: ./ccli delete <catalog_id>")
}
// delete the part if the part id is present
if argPartID != "" {
slog.Debug("deleting part", slog.String("ID", argPartID))
if err := graphql.DeletePart(context.Background(), client, argPartID, argRecursiveMode, argForcedMode); err != nil {
Expand All @@ -55,6 +60,7 @@ func Delete(configFile *config.ConfigData, client *graph.Client, indent string)
return nil
},
}
// adding persistent flags for delete i.e. recursive and force
deleteCmd.PersistentFlags().BoolVarP(&argRecursiveMode, "recursive", "r", false, "To delete parts recursively")
deleteCmd.PersistentFlags().BoolVarP(&argForcedMode, "force", "f", false, "To delete parts forcefully")
return deleteCmd
Expand Down
3 changes: 3 additions & 0 deletions packages/cmd/example.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@ import (
// Example() displays a number of potential calls
// that can be made using the ccli.
func Example() *cobra.Command {
// cobra command for examples
exampleCmd := &cobra.Command{
Use: "examples",
Short: "Ccli is used to interact with the Software Parts Catalog.",
// function to be run on command execution
RunE: func(cmd *cobra.Command, args []string) error {
// list of all the ccli examples that can be executed
exampleString :=
` $ ccli add part openssl-1.1.1n.yml
$ ccli add profile profile_openssl-1.1.1n.yml
Expand Down
Loading

0 comments on commit 1c12c2e

Please sign in to comment.