Skip to content

Commit 6f2ecbe

Browse files
authored
Merge pull request #269 from gatewayd-io/add-plugin-lint-command
Add `plugin lint` command
2 parents eededfd + f0e650d commit 6f2ecbe

File tree

6 files changed

+116
-63
lines changed

6 files changed

+116
-63
lines changed

cmd/config_lint.go

Lines changed: 1 addition & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,7 @@
11
package cmd
22

33
import (
4-
"context"
5-
"encoding/json"
6-
"log"
7-
84
"github.com/gatewayd-io/gatewayd/config"
9-
jsonSchemaGenerator "github.com/invopop/jsonschema"
10-
koanfJson "github.com/knadh/koanf/parsers/json"
11-
jsonSchemaV5 "github.com/santhosh-tekuri/jsonschema/v5"
125
"github.com/spf13/cobra"
136
)
147

@@ -17,49 +10,7 @@ var configLintCmd = &cobra.Command{
1710
Use: "lint",
1811
Short: "Lint the GatewayD global config",
1912
Run: func(cmd *cobra.Command, args []string) {
20-
logger := log.New(cmd.OutOrStdout(), "", 0)
21-
22-
// Load the global configuration file and check it for errors.
23-
conf := config.NewConfig(context.TODO(), globalConfigFile, "")
24-
conf.LoadDefaults(context.TODO())
25-
conf.LoadGlobalConfigFile(context.TODO())
26-
conf.UnmarshalGlobalConfig(context.TODO())
27-
28-
// Marshal the global config to JSON.
29-
jsonData, err := conf.GlobalKoanf.Marshal(koanfJson.Parser())
30-
if err != nil {
31-
logger.Fatal("Error marshalling global config to JSON:\n", err)
32-
}
33-
34-
// Unmarshal the JSON data into a map.
35-
var jsonBytes map[string]interface{}
36-
err = json.Unmarshal(jsonData, &jsonBytes)
37-
if err != nil {
38-
logger.Fatal("Error unmarshalling schema to JSON:\n", err)
39-
}
40-
41-
// Generate a JSON schema from the global config struct.
42-
generatedSchema := jsonSchemaGenerator.Reflect(&config.GlobalConfig{})
43-
44-
// Marshal the schema to JSON.
45-
schemaBytes, err := json.Marshal(generatedSchema)
46-
if err != nil {
47-
logger.Fatal("Error marshalling schema to JSON:\n", err)
48-
}
49-
50-
// Compile the schema for validation.
51-
schema, err := jsonSchemaV5.CompileString("", string(schemaBytes))
52-
if err != nil {
53-
logger.Fatal("Error compiling schema:\n", err)
54-
}
55-
56-
// Validate the global config against the schema.
57-
err = schema.Validate(jsonBytes)
58-
if err != nil {
59-
logger.Fatal("Error validating global config:\n", err)
60-
}
61-
62-
logger.Print("Global config is valid")
13+
lintConfig(cmd, Global, globalConfigFile)
6314
},
6415
}
6516

cmd/plugin_init.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ var pluginInitCmd = &cobra.Command{
1010
Use: "init",
1111
Short: "Create or overwrite the GatewayD plugins config",
1212
Run: func(cmd *cobra.Command, args []string) {
13-
generateConfig(cmd, Plugin, pluginConfigFile, force)
13+
generateConfig(cmd, Plugins, pluginConfigFile, force)
1414
},
1515
}
1616

cmd/plugin_lint.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package cmd
2+
3+
import (
4+
"github.com/gatewayd-io/gatewayd/config"
5+
"github.com/spf13/cobra"
6+
)
7+
8+
// pluginLintCmd represents the plugin lint command.
9+
var pluginLintCmd = &cobra.Command{
10+
Use: "lint",
11+
Short: "Lint the GatewayD plugins config",
12+
Run: func(cmd *cobra.Command, args []string) {
13+
lintConfig(cmd, Plugins, pluginConfigFile)
14+
},
15+
}
16+
17+
func init() {
18+
pluginCmd.AddCommand(pluginLintCmd)
19+
20+
pluginLintCmd.Flags().StringVarP(
21+
&pluginConfigFile, // Already exists in run.go
22+
"plugin-config", "p", config.GetDefaultConfigFilePath(config.PluginsConfigFilename),
23+
"Plugin config file")
24+
}

cmd/utils.go

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@ package cmd
22

33
import (
44
"context"
5+
"encoding/json"
56
"log"
67
"os"
78

89
"github.com/gatewayd-io/gatewayd/config"
10+
jsonSchemaGenerator "github.com/invopop/jsonschema"
911
"github.com/knadh/koanf"
12+
koanfJson "github.com/knadh/koanf/parsers/json"
1013
"github.com/knadh/koanf/parsers/yaml"
14+
jsonSchemaV5 "github.com/santhosh-tekuri/jsonschema/v5"
1115
"github.com/spf13/cobra"
1216
)
1317

@@ -16,8 +20,8 @@ type (
1620
)
1721

1822
var (
19-
Global configFileType = "global"
20-
Plugin configFileType = "plugin"
23+
Global configFileType = "global"
24+
Plugins configFileType = "plugins"
2125
)
2226

2327
// generateConfig generates a config file of the given type.
@@ -36,7 +40,7 @@ func generateConfig(cmd *cobra.Command, fileType configFileType, configFile stri
3640
switch fileType {
3741
case Global:
3842
konfig = conf.GlobalKoanf
39-
case Plugin:
43+
case Plugins:
4044
konfig = conf.PluginKoanf
4145
default:
4246
logger.Fatal("Invalid config file type")
@@ -65,3 +69,77 @@ func generateConfig(cmd *cobra.Command, fileType configFileType, configFile stri
6569
}
6670
logger.Printf("Config file '%s' was %s successfully.", configFile, verb)
6771
}
72+
73+
func lintConfig(cmd *cobra.Command, fileType configFileType, configFile string) {
74+
logger := log.New(cmd.OutOrStdout(), "", 0)
75+
76+
// Load the config file and check it for errors.
77+
var conf *config.Config
78+
switch fileType {
79+
case Global:
80+
conf = config.NewConfig(context.TODO(), configFile, "")
81+
conf.LoadDefaults(context.TODO())
82+
conf.LoadGlobalConfigFile(context.TODO())
83+
conf.UnmarshalGlobalConfig(context.TODO())
84+
case Plugins:
85+
conf = config.NewConfig(context.TODO(), "", configFile)
86+
conf.LoadDefaults(context.TODO())
87+
conf.LoadPluginConfigFile(context.TODO())
88+
conf.UnmarshalPluginConfig(context.TODO())
89+
default:
90+
logger.Fatal("Invalid config file type")
91+
}
92+
93+
// Marshal the config to JSON.
94+
var jsonData []byte
95+
var err error
96+
switch fileType {
97+
case Global:
98+
jsonData, err = conf.GlobalKoanf.Marshal(koanfJson.Parser())
99+
case Plugins:
100+
jsonData, err = conf.PluginKoanf.Marshal(koanfJson.Parser())
101+
default:
102+
logger.Fatal("Invalid config file type")
103+
}
104+
if err != nil {
105+
logger.Fatalf("Error marshalling %s config to JSON: %s\n", string(fileType), err)
106+
}
107+
108+
// Unmarshal the JSON data into a map.
109+
var jsonBytes map[string]interface{}
110+
err = json.Unmarshal(jsonData, &jsonBytes)
111+
if err != nil {
112+
logger.Fatal("Error unmarshalling schema to JSON:\n", err)
113+
}
114+
115+
// Generate a JSON schema from the config struct.
116+
var generatedSchema *jsonSchemaGenerator.Schema
117+
switch fileType {
118+
case Global:
119+
generatedSchema = jsonSchemaGenerator.Reflect(&config.GlobalConfig{})
120+
case Plugins:
121+
generatedSchema = jsonSchemaGenerator.Reflect(&config.PluginConfig{})
122+
default:
123+
logger.Fatal("Invalid config file type")
124+
}
125+
126+
// Marshal the schema to JSON.
127+
schemaBytes, err := json.Marshal(generatedSchema)
128+
if err != nil {
129+
logger.Fatal("Error marshalling schema to JSON:\n", err)
130+
}
131+
132+
// Compile the schema for validation.
133+
schema, err := jsonSchemaV5.CompileString("", string(schemaBytes))
134+
if err != nil {
135+
logger.Fatal("Error compiling schema:\n", err)
136+
}
137+
138+
// Validate the config against the schema.
139+
err = schema.Validate(jsonBytes)
140+
if err != nil {
141+
logger.Fatalf("Error validating %s config: %s\n", string(fileType), err)
142+
}
143+
144+
logger.Printf("%s config is valid\n", fileType)
145+
}

config/constants.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const (
1919
Stopped
2020
)
2121

22-
// Policy is the policy for hook verification.
22+
// VerificationPolicy is the policy for hook verification.
2323
const (
2424
// Non-strict (permissive) mode.
2525
PassDown VerificationPolicy = "passdown" // Pass down the extra keys/values in result to the next plugins

config/types.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,19 @@ import (
1616
// }
1717

1818
type Plugin struct {
19-
Name string `json:"name"`
19+
Name string `json:"name" jsonschema:"required"`
2020
Enabled bool `json:"enabled"`
21-
LocalPath string `json:"localPath"`
21+
LocalPath string `json:"localPath" jsonschema:"required"`
2222
Args []string `json:"args"`
23-
Env []string `json:"env"`
24-
Checksum string `json:"checksum"`
23+
Env []string `json:"env" jsonschema:"required"`
24+
Checksum string `json:"checksum" jsonschema:"required"`
2525
}
2626

2727
type PluginConfig struct {
28-
VerificationPolicy string `json:"verificationPolicy"`
29-
CompatibilityPolicy string `json:"compatibilityPolicy"`
30-
AcceptancePolicy string `json:"acceptancePolicy"`
31-
TerminationPolicy string `json:"terminationPolicy"`
28+
VerificationPolicy string `json:"verificationPolicy" jsonschema:"enum=passdown,enum=ignore,enum=abort,enum=remove"`
29+
CompatibilityPolicy string `json:"compatibilityPolicy" jsonschema:"enum=strict,enum=loose"`
30+
AcceptancePolicy string `json:"acceptancePolicy" jsonschema:"enum=accept,enum=reject"`
31+
TerminationPolicy string `json:"terminationPolicy" jsonschema:"enum=continue,enum=stop"`
3232
EnableMetricsMerger bool `json:"enableMetricsMerger"`
3333
MetricsMergerPeriod time.Duration `json:"metricsMergerPeriod" jsonschema:"oneof_type=string;integer"`
3434
HealthCheckPeriod time.Duration `json:"healthCheckPeriod" jsonschema:"oneof_type=string;integer"`

0 commit comments

Comments
 (0)