{"id":20410280,"url":"https://github.com/kuadrant/policy-machinery","last_synced_at":"2025-04-12T15:53:20.443Z","repository":{"id":246493461,"uuid":"820926057","full_name":"Kuadrant/policy-machinery","owner":"Kuadrant","description":"Machinery for implementing Gateway API policies","archived":false,"fork":false,"pushed_at":"2024-10-25T16:29:22.000Z","size":530,"stargazers_count":8,"open_issues_count":5,"forks_count":2,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-10-25T17:50:09.329Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Kuadrant.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-27T13:02:13.000Z","updated_at":"2024-10-25T16:28:34.000Z","dependencies_parsed_at":"2024-06-28T09:44:21.414Z","dependency_job_id":"0bb78b7d-00fe-45d7-bd53-519574392ecc","html_url":"https://github.com/Kuadrant/policy-machinery","commit_stats":null,"previous_names":["kuadrant/policy-machinery"],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kuadrant%2Fpolicy-machinery","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kuadrant%2Fpolicy-machinery/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kuadrant%2Fpolicy-machinery/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Kuadrant%2Fpolicy-machinery/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Kuadrant","download_url":"https://codeload.github.com/Kuadrant/policy-machinery/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248592153,"owners_count":21130193,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-15T05:45:18.411Z","updated_at":"2025-04-12T15:53:20.416Z","avatar_url":"https://github.com/Kuadrant.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Policy Machinery\n\nMachinery for implementing [Gateway API](https://gateway-api.sigs.k8s.io/reference/policy-attachment/) policies and policy controllers.\n\n## Features\n- Types for modeling topologies of targetable network resources and corresponding attached policies\n- Examples of policy types defined with their own custom merge strategies (JSON patch, [Kuadrant Defaults \u0026 Overrides](https://docs.kuadrant.io/0.10.0/architecture/rfcs/0009-defaults-and-overrides))\n- Helpers for building and testing Gateway API-specific topologies of network resources and policies\n- Special package for implementing custom controllers of Gateway API resources, policies and related runtime objects\n- 2 patterns for reconciliation: state-of-the-world and incremental events\n- Reconciliation workflows (for handling dependencies and concurrent tasks) and subscriptions\n- [Full example](./examples/kuadrant/README.md) of custom controller leveraging a Gateway API topology with 4 kinds of policies\n\n## Usage\n\n❶ Import the package:\n\n```sh\ngo get github.com/kuadrant/policy-machinery\n```\n\n❷ Implement the `Policy` interface:\n\n```go\npackage mypolicy\n\nimport (\n  \"github.com/kuadrant/policy-machinery/machinery\"\n  metav1 \"k8s.io/apimachinery/pkg/apis/meta/v1\"\n  gwapiv1alpha2 \"sigs.k8s.io/gateway-api/apis/v1alpha2\"\n)\n\nvar _ machinery.Policy = \u0026MyPolicy{}\n\ntype MyPolicy struct {\n\tmetav1.TypeMeta   `json:\",inline\"`\n\tmetav1.ObjectMeta `json:\"metadata,omitempty\"`\n\n\tSpec MyPolicySpec `json:\"spec\"`\n}\n\ntype MyPolicySpec struct {\n  TargetRef gwapiv1alpha2.LocalPolicyTargetReference\n}\n\nfunc (p *MyPolicy) GetLocator() string {\n\treturn machinery.LocatorFromObject(p)\n}\n\nfunc (p *MyPolicy) GetTargetRefs() []machinery.PolicyTargetReference {\n\treturn []machinery.PolicyTargetReference{\n\t\tmachinery.LocalPolicyTargetReference{\n\t\t\tLocalPolicyTargetReference: p.Spec.TargetRef,\n\t\t\tPolicyNamespace: p.Namespace,\n\t\t},\n\t}\n}\n\nfunc (p *MyPolicy) GetMergeStrategy() machinery.MergeStrategy {\n\treturn machinery.DefaultMergeStrategy // replace with your merge strategy\n}\n\nfunc (p *MyPolicy) Merge(policy machinery.Policy) machinery.Policy {\n\tsource := policy.(*MyPolicy)\n\treturn source.GetMergeStrategy()(source, p)\n}\n\nfunc (p *MyPolicy) DeepCopy() *MyPolicy {\n\tspec := p.Spec.DeepCopy()\n\treturn \u0026MyPolicy{\n\t\tTypeMeta:   p.TypeMeta,\n\t\tObjectMeta: p.ObjectMeta,\n\t\tSpec:       *spec,\n\t}\n}\n```\n\n❸ Build a topology of targetable network resources:\n\n```go\nimport (\n  \"fmt\"\n\n  \"github.com/kuadrant/policy-machinery/machinery\"\n  \"github.com/samber/lo\"\n)\n\n// ...\n\ntopology := machinery.NewTopology(\n  machinery.WithTargetables(gateways...),\n  machinery.WithTargetables(httpRoutes...),\n  machinery.WithTargetables(services...),\n  machinery.WithLinks(\n    machinery.LinkGatewayToHTTPRouteFunc(gateways),\n    machinery.LinkHTTPRouteToServiceFunc(httpRoutes, false),\n  ),\n  machinery.WithPolicies(policies...),\n)\n\n// Print the topology in Graphviz DOT language\nfmt.Println(topology.ToDot())\n\n// Calculate the effective policy for any path between 2 targetables in the topology\npaths := topology.Paths(gateways[0], services[0])\n\nfor _, path := range paths {\n  // Gather all policies in the path sorted from the least specific to the most specific\n  policies := lo.FlatMap(path, func(targetable machinery.Targetable, _ int) []machinery.Policy {\n    return targetable.Policies()\n  })\n\n  // Map reduces the policies from most specific to least specific, merging them into one effective policy for each path\n  var emptyPolicy machinery.Policy = \u0026MyPolicy{}\n  effectivePolicy := lo.ReduceRight(policies, func(effectivePolicy machinery.Policy, policy machinery.Policy, _ int) machinery.Policy {\n    return effectivePolicy.Merge(policy)\n  }, emptyPolicy)\n}\n```\n\n### Topology of Gateway API resources\n\nAlternatively to the generic `machinery.NewTopology(…)`, use `machinery.NewGatewayAPITopology(…)` to build a topology of Gateway API resources.\nThe links between network objects (GatewayClasses, Gateways, HTTPRoutes, etc) will be inferred automatically.\n\n```go\ntopology := machinery.NewGatewayAPITopology(\n  machinery.WithGateways(gateways...),\n  machinery.WithHTTPRoutes(httpRoutes...),\n  machinery.WithServices(services...),\n  machinery.WithPolicies(policies...),\n)\n```\n\nYou can use the topology options `ExpandGatewayListeners()`, `ExpandHTTPRouteRules()`, and\n`ExpandServicePorts()` to automatically expand Gateways, HTTPRoutes and Services. The inner sections of these resources\n(listeners, route rules, service ports, respectively) will be added as targetables to the topology. The links between objects\nare then automatically adjusted accordingly.\n\n### Custom controllers for topologies of Gateway API resources\n\nThe `controller` package defines a simplified controller abstraction based on the [k8s.io/apimachinery](https://pkg.go.dev/k8s.io/apimachinery)\nand [sigs.k8s.io/controller-runtime](https://pkg.go.dev/sigs.k8s.io/controller-runtime), that builds an in-memory\nrepresentation of your topology of Gateway API, policy and generic linked resources, from events watched from the\ncluster.\n\nExample:\n\n```go\nimport (\n  \"context\"\n  \"sync\"\n\n  \"k8s.io/apimachinery/pkg/runtime/schema\"\n  \"k8s.io/client-go/dynamic\"\n  \"k8s.io/client-go/tools/clientcmd\"\n  ctrlruntime \"sigs.k8s.io/controller-runtime\"\n\n  gwapiv1 \"sigs.k8s.io/gateway-api/apis/v1\"\n  \"./mypolicy\"\n\n  \"github.com/kuadrant/policy-machinery/controller\"\n\t\"github.com/kuadrant/policy-machinery/machinery\"\n)\n\nfunc main() {\n\t// create a logger\n\tlogger := controller.CreateAndSetLogger()\n\n\t// load kubeconfig\n\tkubeconfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(clientcmd.NewDefaultClientConfigLoadingRules(), \u0026clientcmd.ConfigOverrides{})\n\tconfig, err := kubeconfig.ClientConfig()\n\tif err != nil {\n\t\tlog.Fatalf(\"Error loading kubeconfig: %v\", err)\n\t}\n\n\t// create a client\n\tclient, err := dynamic.NewForConfig(config)\n\tif err != nil {\n\t\tlog.Fatalf(\"Error creating client: %v\", err)\n\t}\n\n\t// create a controller with a built-in gateway api topology\n\tcontroller := controller.NewController(\n\t\tcontroller.WithLogger(logger),\n\t\tcontroller.WithClient(client),\n\t\tcontroller.WithRunnable(\"gateway\", controller.Watch(*gwapiv1.Gateway{}, gwapiv1.SchemeGroupVersion.WithResource(\"gateways\"), metav1.NamespaceAll)),\n\t\tcontroller.WithRunnable(\"httproute\", controller.Watch(*gwapiv1.HTTPRoute{}, gwapiv1.SchemeGroupVersion.WithResource(\"httproutes\"), metav1.NamespaceAll)),\n\t\tcontroller.WithRunnable(\"mypolicy\", controller.Watch(*mypolicy.MyPolicy{}, mypolicy.SchemeGroupVersion.WithResource(\"mypolicies\"), metav1.NamespaceAll)),\n\t\tcontroller.WithPolicyKinds(schema.GroupKind{Group: mypolicy.SchemeGroupVersion.Group, Kind: \"MyPolicy\"}),\n\t\tcontroller.WithReconcile(reconcile),\n\t)\n\n\tif err := controller.Start(ctrlruntime.SetupSignalHandler()); err != nil {\n\t\tlogger.Error(err, \"error starting controller\")\n\t\tos.Exit(1)\n\t}\n}\n\nfunc reconcile(ctx context.Context, events []ResourceEvent, topology *machinery.Topology, err error, state *sync.Map) error {\n  // TODO: implement your reconciliation business logic here\n}\n```\n\n[State-of-the-world](https://pkg.go.dev/github.com/kuadrant/policy-machinery/controller#StateReconciler) kind of watchers will coallesce events into a compact single reconciliation call (default). Alternatively, use [incremental informers](https://pkg.go.dev/github.com/kuadrant/policy-machinery/controller#IncrementalInformer) for more granular reconciliation calls at every event.\n\nCheck out as well the options for defining watchers with [predicates](https://pkg.go.dev/github.com/kuadrant/policy-machinery/controller#RunnableBuilderOptions) (labels, field selectors, etc).\n\nFor more advanced and optimized reconciliation, consider [workflows](https://pkg.go.dev/github.com/kuadrant/policy-machinery/controller#Workflow) (for handling dependencies and concurrent tasks) and [subscriptions](https://pkg.go.dev/github.com/kuadrant/policy-machinery/controller#Subscription) (for macthing on specific event types).\n\n## Example\n\nCheck out the full [Kuadrant example](./examples/kuadrant/README.md) of using the\n`machinery` and `controller` packages to implement a full custom controller that watches for\nGateway API resources, as well as policy resources of 4 kinds (DNSPolicy, TLSPolicy, AuthPolicy and RateLimitPolicy.)\n\nEach event the controller subscribes to generates a call to a reconcile function that saves a graphical representation\nof the updated Gateway API topology to a file in the fle system, and computes all applicable effective policies for\nall 4 kinds of policies.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkuadrant%2Fpolicy-machinery","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkuadrant%2Fpolicy-machinery","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkuadrant%2Fpolicy-machinery/lists"}