generated from cybozu-go/neco-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Implement manifest command Signed-off-by: Daichi Sakaue <[email protected]> Co-authored-by: Tomoki Sugiura <[email protected]>
- Loading branch information
1 parent
c57e825
commit 10f88e5
Showing
12 changed files
with
498 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package app | ||
|
||
import "github.com/spf13/cobra" | ||
|
||
func init() { | ||
rootCmd.AddCommand(manifestCmd) | ||
} | ||
|
||
var manifestCmd = &cobra.Command{ | ||
Use: "manifest", | ||
Short: "Generate CiliumNetworkPolicy", | ||
Long: `Generate CiliumNetworkPolicy`, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
package app | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
"io" | ||
"strconv" | ||
|
||
"github.com/spf13/cobra" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
"sigs.k8s.io/yaml" | ||
) | ||
|
||
var manifestGenerateOptions struct { | ||
name string | ||
egress bool | ||
ingress bool | ||
allow bool | ||
deny bool | ||
from string | ||
to string | ||
} | ||
|
||
func init() { | ||
manifestGenerateCmd.Flags().StringVar(&manifestGenerateOptions.name, "name", "", "resource name") | ||
manifestGenerateCmd.Flags().BoolVar(&manifestGenerateOptions.egress, "egress", false, "generate egress rule") | ||
manifestGenerateCmd.Flags().BoolVar(&manifestGenerateOptions.ingress, "ingress", false, "generate ingress rule") | ||
manifestGenerateCmd.Flags().BoolVar(&manifestGenerateOptions.allow, "allow", false, "generate allow rule") | ||
manifestGenerateCmd.Flags().BoolVar(&manifestGenerateOptions.deny, "deny", false, "generate deny rule") | ||
manifestGenerateCmd.Flags().StringVar(&manifestGenerateOptions.from, "from", "", "egress pod") | ||
manifestGenerateCmd.Flags().StringVar(&manifestGenerateOptions.to, "to", "", "ingress pod") | ||
manifestCmd.AddCommand(manifestGenerateCmd) | ||
} | ||
|
||
var manifestGenerateCmd = &cobra.Command{ | ||
Use: "generate", | ||
Short: "Generate CiliumNetworkPolicy", | ||
Long: `Generate CiliumNetworkPolicy`, | ||
|
||
Args: cobra.ExactArgs(0), | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
return runManifestGenerate(context.Background(), cmd.OutOrStdout()) | ||
}, | ||
} | ||
|
||
func runManifestGenerate(ctx context.Context, w io.Writer) error { | ||
egress := manifestGenerateOptions.egress | ||
ingress := manifestGenerateOptions.ingress | ||
allow := manifestGenerateOptions.allow | ||
deny := manifestGenerateOptions.deny | ||
from := manifestGenerateOptions.from | ||
to := manifestGenerateOptions.to | ||
|
||
if egress == ingress { | ||
return errors.New("one of --egress or --ingress should be specified") | ||
} | ||
if allow == deny { | ||
return errors.New("one of --allow or --deny should be specified") | ||
} | ||
|
||
sub, err := parseNamespacedName(from) | ||
if err != nil { | ||
return errors.New("--from and --to should be specified as NAMESPACE/POD") | ||
} | ||
|
||
obj, err := parseNamespacedName(to) | ||
if err != nil { | ||
return errors.New("--from and --to should be specified as NAMESPACE/POD") | ||
} | ||
|
||
if ingress { | ||
sub, obj = obj, sub | ||
} | ||
|
||
// Parameters are all up, let's start querying API server | ||
_, dynamicClient, err := createK8sClients() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
subIdentity, err := getPodIdentity(ctx, dynamicClient, sub.Namespace, sub.Name) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
subResource, err := dynamicClient.Resource(gvrIdentity).Get(ctx, strconv.Itoa(int(subIdentity)), metav1.GetOptions{}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
subLabels, ok, err := unstructured.NestedStringMap(subResource.Object, "security-labels") | ||
if err != nil { | ||
return err | ||
} | ||
if !ok { | ||
return fmt.Errorf("pod %s/%s is not assigned security labels", sub.Namespace, sub.Name) | ||
} | ||
|
||
objIdentity, err := getPodIdentity(ctx, dynamicClient, obj.Namespace, obj.Name) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
objResource, err := dynamicClient.Resource(gvrIdentity).Get(ctx, strconv.Itoa(int(objIdentity)), metav1.GetOptions{}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
objLabels, ok, err := unstructured.NestedStringMap(objResource.Object, "security-labels") | ||
if err != nil { | ||
return err | ||
} | ||
if !ok { | ||
return fmt.Errorf("pod %s/%s is not assigned security labels", obj.Namespace, obj.Name) | ||
} | ||
|
||
policyName := manifestGenerateOptions.name | ||
if policyName == "" { | ||
direction := "egress" | ||
policy := "allow" | ||
if ingress { | ||
direction = "ingress" | ||
} | ||
if deny { | ||
policy = "deny" | ||
} | ||
policyName = fmt.Sprintf("%s-%s-%d-%d", direction, policy, subIdentity, objIdentity) | ||
} | ||
|
||
var manifest unstructured.Unstructured | ||
manifest.SetGroupVersionKind(gvkNetworkPolicy) | ||
manifest.SetNamespace(sub.Namespace) | ||
manifest.SetName(policyName) | ||
err = unstructured.SetNestedStringMap(manifest.Object, subLabels, "spec", "endpointSelector", "matchLabels") | ||
if err != nil { | ||
return err | ||
} | ||
|
||
objMap := make(map[string]any) | ||
for k, v := range objLabels { | ||
objMap[k] = v | ||
} | ||
|
||
var section, field string | ||
switch { | ||
case egress && allow: | ||
section = "egress" | ||
field = "toEndpoints" | ||
case egress && deny: | ||
section = "egressDeny" | ||
field = "toEndpoints" | ||
case ingress && allow: | ||
section = "ingress" | ||
field = "fromEndpoints" | ||
case ingress && deny: | ||
section = "ingressDeny" | ||
field = "fromEndpoints" | ||
} | ||
|
||
err = unstructured.SetNestedField(manifest.Object, []any{ | ||
map[string]any{ | ||
field: []any{ | ||
map[string]any{ | ||
"matchLabels": objMap, | ||
}, | ||
}, | ||
}, | ||
}, "spec", section) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
data, err := yaml.Marshal(manifest.Object) | ||
if err != nil { | ||
return err | ||
} | ||
if _, err := fmt.Fprintf(w, "%s", string(data)); err != nil { | ||
return err | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package app | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"io" | ||
"sort" | ||
"strings" | ||
|
||
"github.com/spf13/cobra" | ||
) | ||
|
||
var manifestRangeOptions struct { | ||
from string | ||
to string | ||
} | ||
|
||
func init() { | ||
manifestRangeCmd.Flags().StringVar(&manifestRangeOptions.from, "from", "", "egress pod") | ||
manifestRangeCmd.Flags().StringVar(&manifestRangeOptions.to, "to", "", "ingress pod") | ||
manifestCmd.AddCommand(manifestRangeCmd) | ||
} | ||
|
||
var manifestRangeCmd = &cobra.Command{ | ||
Use: "range", | ||
Short: "List affected pods of a generated manifest", | ||
Long: `List affected pods of a generated manifest`, | ||
|
||
Args: cobra.ExactArgs(0), | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
return runManifestRange(context.Background(), cmd.OutOrStdout()) | ||
}, | ||
} | ||
|
||
type manifestRangeEntry struct { | ||
Part string `json:"part"` | ||
Namespace string `json:"namespace"` | ||
Name string `json:"name"` | ||
} | ||
|
||
func lessManifestRangeEntry(x, y *manifestRangeEntry) bool { | ||
ret := strings.Compare(x.Part, y.Part) | ||
if ret == 0 { | ||
ret = strings.Compare(x.Namespace, y.Namespace) | ||
} | ||
if ret == 0 { | ||
ret = strings.Compare(x.Name, y.Name) | ||
} | ||
return ret < 0 | ||
} | ||
|
||
func runManifestRange(ctx context.Context, w io.Writer) error { | ||
if manifestRangeOptions.from == "" || manifestRangeOptions.to == "" { | ||
return errors.New("--from and --to options are required") | ||
} | ||
|
||
from, err := parseNamespacedName(manifestRangeOptions.from) | ||
if err != nil { | ||
return errors.New("--from and --to should be specified as NAMESPACE/POD") | ||
} | ||
|
||
to, err := parseNamespacedName(manifestRangeOptions.to) | ||
if err != nil { | ||
return errors.New("--from and --to should be specified as NAMESPACE/POD") | ||
} | ||
|
||
_, dynamicClient, err := createK8sClients() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
fromIdentity, err := getPodIdentity(ctx, dynamicClient, from.Namespace, from.Name) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
toIdentity, err := getPodIdentity(ctx, dynamicClient, to.Namespace, to.Name) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
idEndpoints, err := getIdentityEndpoints(ctx, dynamicClient) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
arr := make([]manifestRangeEntry, 0) | ||
sort.Slice(arr, func(i, j int) bool { return lessManifestRangeEntry(&arr[i], &arr[j]) }) | ||
|
||
for _, ep := range idEndpoints[int(fromIdentity)] { | ||
entry := manifestRangeEntry{ | ||
Part: "From", | ||
Namespace: ep.GetNamespace(), | ||
Name: ep.GetName(), | ||
} | ||
arr = append(arr, entry) | ||
} | ||
for _, ep := range idEndpoints[int(toIdentity)] { | ||
entry := manifestRangeEntry{ | ||
Part: "To", | ||
Namespace: ep.GetNamespace(), | ||
Name: ep.GetName(), | ||
} | ||
arr = append(arr, entry) | ||
} | ||
return writeSimpleOrJson(w, arr, []string{"PART", "NAMESPACE", "NAME"}, len(arr), func(index int) []any { | ||
ep := arr[index] | ||
return []any{ep.Part, ep.Namespace, ep.Name} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.