add IRSA api

This commit is contained in:
kkb0318
2024-05-15 21:58:49 +09:00
parent 811d295b74
commit a3d160c47c
16 changed files with 647 additions and 5 deletions
+9
View File
@@ -20,4 +20,13 @@ resources:
kind: IRSASetup
path: github.com/kkb0318/irsa-manager/api/v1alpha1
version: v1alpha1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: kkb0318.github.io
group: irsa
kind: IRSA
path: github.com/kkb0318/irsa-manager/api/v1alpha1
version: v1alpha1
version: "3"
+15 -3
View File
@@ -3,8 +3,20 @@
TODO
- [x] delete secret
- [x] delete cloud resource
- [ ] only once secret creation (status check)
- [x] delete s3
- [x] only once secret creation (status check)
- [x] no update secret
- [ ] no update bucket object
- [x] no update bucket object
- [x] delete idp
- [x] issue: when irsasetup was deleted, resource remained with some error occured
- [x] certificate
- [ ] check keys.json keyid has to be empty or not
- [ ] IRSA api
- [ ] use with cert-manager
- [ ] validation webhook (invalid to change)
```
kubectl get secret -n kube-system irsa-manager-key -o jsonpath="{.data.ssh-privatekey}" | base64 --decode | sudo tee /etc/kubernetes/pki/irsa-manager.key > /dev/null
kubectl get secret -n kube-system irsa-manager-key -o jsonpath="{.data.ssh-publickey}" | base64 --decode | sudo tee /etc/kubernetes/pki/irsa-manager.pub > /dev/null
```
+81
View File
@@ -0,0 +1,81 @@
/*
Copyright 2024.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// IRSASpec defines the desired state of IRSA
type IRSASpec struct {
// ServiceAccount represents the Kubernetes service account associated with the IRSA
ServiceAccount IRSAServiceAccount `json:"serviceAccount,omitempty"`
// IamRole represents the IAM role details associated with the IRSA
IamRole IamRole `json:"iamRole,omitempty"`
// IamPolicies represents the list of IAM policies to be attached to the IAM role
IamPolicies []string `json:"iamPolicies,omitempty"`
}
// IRSAServiceAccount represents the details of the Kubernetes service account
type IRSAServiceAccount struct {
// Name represents the name of the Kubernetes service account
Name string `json:"name,omitempty"`
// Namespaces represents the list of namespaces where the service account is used
Namespaces []string `json:"namespaces,omitempty"`
}
// IamRole represents the IAM role configuration
type IamRole struct {
// Create specifies whether to create the IAM role or not
Create bool `json:"create,omitempty"`
// Name represents the name of the IAM role
Name string `json:"name,omitempty"`
}
// IRSAStatus defines the observed state of IRSA
type IRSAStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
}
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
// IRSA is the Schema for the irsas API
type IRSA struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec IRSASpec `json:"spec,omitempty"`
Status IRSAStatus `json:"status,omitempty"`
}
//+kubebuilder:object:root=true
// IRSAList contains a list of IRSA
type IRSAList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []IRSA `json:"items"`
}
func init() {
SchemeBuilder.Register(&IRSA{}, &IRSAList{})
}
+131
View File
@@ -57,6 +57,85 @@ func (in *Discovery) DeepCopy() *Discovery {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IRSA) DeepCopyInto(out *IRSA) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IRSA.
func (in *IRSA) DeepCopy() *IRSA {
if in == nil {
return nil
}
out := new(IRSA)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *IRSA) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IRSAList) DeepCopyInto(out *IRSAList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]IRSA, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IRSAList.
func (in *IRSAList) DeepCopy() *IRSAList {
if in == nil {
return nil
}
out := new(IRSAList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *IRSAList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IRSAServiceAccount) DeepCopyInto(out *IRSAServiceAccount) {
*out = *in
if in.Namespaces != nil {
in, out := &in.Namespaces, &out.Namespaces
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IRSAServiceAccount.
func (in *IRSAServiceAccount) DeepCopy() *IRSAServiceAccount {
if in == nil {
return nil
}
out := new(IRSAServiceAccount)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IRSASetup) DeepCopyInto(out *IRSASetup) {
*out = *in
@@ -155,6 +234,58 @@ func (in *IRSASetupStatus) DeepCopy() *IRSASetupStatus {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IRSASpec) DeepCopyInto(out *IRSASpec) {
*out = *in
in.ServiceAccount.DeepCopyInto(&out.ServiceAccount)
out.IamRole = in.IamRole
if in.IamPolicies != nil {
in, out := &in.IamPolicies, &out.IamPolicies
*out = make([]string, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IRSASpec.
func (in *IRSASpec) DeepCopy() *IRSASpec {
if in == nil {
return nil
}
out := new(IRSASpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IRSAStatus) DeepCopyInto(out *IRSAStatus) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IRSAStatus.
func (in *IRSAStatus) DeepCopy() *IRSAStatus {
if in == nil {
return nil
}
out := new(IRSAStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *IamRole) DeepCopyInto(out *IamRole) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IamRole.
func (in *IamRole) DeepCopy() *IamRole {
if in == nil {
return nil
}
out := new(IamRole)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *S3Discovery) DeepCopyInto(out *S3Discovery) {
*out = *in
+7
View File
@@ -129,6 +129,13 @@ func main() {
setupLog.Error(err, "unable to create controller", "controller", "IRSASetup")
os.Exit(1)
}
if err = (&controller.IRSAReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "IRSA")
os.Exit(1)
}
//+kubebuilder:scaffold:builder
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
@@ -0,0 +1,83 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.14.0
name: irsas.irsa.kkb0318.github.io
spec:
group: irsa.kkb0318.github.io
names:
kind: IRSA
listKind: IRSAList
plural: irsas
singular: irsa
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
description: IRSA is the Schema for the irsas API
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
description: IRSASpec defines the desired state of IRSA
properties:
iamPolicies:
description: IamPolicies represents the list of IAM policies to be
attached to the IAM role
items:
type: string
type: array
iamRole:
description: IamRole represents the IAM role details associated with
the IRSA
properties:
create:
description: Create specifies whether to create the IAM role or
not
type: boolean
name:
description: Name represents the name of the IAM role
type: string
type: object
serviceAccount:
description: ServiceAccount represents the Kubernetes service account
associated with the IRSA
properties:
name:
description: Name represents the name of the Kubernetes service
account
type: string
namespaces:
description: Namespaces represents the list of namespaces where
the service account is used
items:
type: string
type: array
type: object
type: object
status:
description: IRSAStatus defines the observed state of IRSA
type: object
type: object
served: true
storage: true
subresources:
status: {}
+3
View File
@@ -3,17 +3,20 @@
# It should be run by config/default
resources:
- bases/irsa.kkb0318.github.io_irsasetups.yaml
- bases/irsa.kkb0318.github.io_irsas.yaml
#+kubebuilder:scaffold:crdkustomizeresource
patches:
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
# patches here are for enabling the conversion webhook for each CRD
#- path: patches/webhook_in_irsasetups.yaml
#- path: patches/webhook_in_irsas.yaml
#+kubebuilder:scaffold:crdkustomizewebhookpatch
# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix.
# patches here are for enabling the CA injection for each CRD
#- path: patches/cainjection_in_irsasetups.yaml
#- path: patches/cainjection_in_irsas.yaml
#+kubebuilder:scaffold:crdkustomizecainjectionpatch
# [WEBHOOK] To enable webhook, uncomment the following section
+31
View File
@@ -0,0 +1,31 @@
# permissions for end users to edit irsas.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: clusterrole
app.kubernetes.io/instance: irsa-editor-role
app.kubernetes.io/component: rbac
app.kubernetes.io/created-by: irsa-manager
app.kubernetes.io/part-of: irsa-manager
app.kubernetes.io/managed-by: kustomize
name: irsa-editor-role
rules:
- apiGroups:
- irsa.kkb0318.github.io
resources:
- irsas
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- irsa.kkb0318.github.io
resources:
- irsas/status
verbs:
- get
+27
View File
@@ -0,0 +1,27 @@
# permissions for end users to view irsas.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
app.kubernetes.io/name: clusterrole
app.kubernetes.io/instance: irsa-viewer-role
app.kubernetes.io/component: rbac
app.kubernetes.io/created-by: irsa-manager
app.kubernetes.io/part-of: irsa-manager
app.kubernetes.io/managed-by: kustomize
name: irsa-viewer-role
rules:
- apiGroups:
- irsa.kkb0318.github.io
resources:
- irsas
verbs:
- get
- list
- watch
- apiGroups:
- irsa.kkb0318.github.io
resources:
- irsas/status
verbs:
- get
+26
View File
@@ -76,6 +76,32 @@ rules:
- patch
- update
- watch
- apiGroups:
- irsa.kkb0318.github.io
resources:
- irsas
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- irsa.kkb0318.github.io
resources:
- irsas/finalizers
verbs:
- update
- apiGroups:
- irsa.kkb0318.github.io
resources:
- irsas/status
verbs:
- get
- patch
- update
- apiGroups:
- irsa.kkb0318.github.io
resources:
+12
View File
@@ -0,0 +1,12 @@
apiVersion: irsa.kkb0318.github.io/v1alpha1
kind: IRSA
metadata:
labels:
app.kubernetes.io/name: irsa
app.kubernetes.io/instance: irsa-sample
app.kubernetes.io/part-of: irsa-manager
app.kubernetes.io/managed-by: kustomize
app.kubernetes.io/created-by: irsa-manager
name: irsa-sample
spec:
# TODO(user): Add fields here
+1
View File
@@ -1,4 +1,5 @@
## Append samples of your project ##
resources:
- irsa_v1alpha1_irsasetup.yaml
- irsa_v1alpha1_irsa.yaml
#+kubebuilder:scaffold:manifestskustomizesamples
+74 -1
View File
@@ -9,6 +9,7 @@
Package v1alpha1 contains API Schema definitions for the irsa v1alpha1 API group
### Resource Types
- [IRSA](#irsa)
- [IRSASetup](#irsasetup)
@@ -46,6 +47,41 @@ _Appears in:_
| `s3` _[S3Discovery](#s3discovery)_ | S3 specifies the AWS S3 bucket details where the OIDC provider's discovery information is hosted. | | |
#### IRSA
IRSA is the Schema for the irsas API
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `apiVersion` _string_ | `irsa.kkb0318.github.io/v1alpha1` | | |
| `kind` _string_ | `IRSA` | | |
| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | |
| `spec` _[IRSASpec](#irsaspec)_ | | | |
#### IRSAServiceAccount
IRSAServiceAccount represents the details of the Kubernetes service account
_Appears in:_
- [IRSASpec](#irsaspec)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `name` _string_ | Name represents the name of the Kubernetes service account | | |
| `namespaces` _string array_ | Namespaces represents the list of namespaces where the service account is used | | |
#### IRSASetup
@@ -60,7 +96,7 @@ IRSASetup represents a configuration for setting up IAM Roles for Service Accoun
| --- | --- | --- | --- |
| `apiVersion` _string_ | `irsa.kkb0318.github.io/v1alpha1` | | |
| `kind` _string_ | `IRSASetup` | | |
| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | |
| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | |
| `spec` _[IRSASetupSpec](#irsasetupspec)_ | | | |
@@ -85,6 +121,43 @@ _Appears in:_
#### IRSASpec
IRSASpec defines the desired state of IRSA
_Appears in:_
- [IRSA](#irsa)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `serviceAccount` _[IRSAServiceAccount](#irsaserviceaccount)_ | ServiceAccount represents the Kubernetes service account associated with the IRSA | | |
| `iamRole` _[IamRole](#iamrole)_ | IamRole represents the IAM role details associated with the IRSA | | |
| `iamPolicies` _string array_ | IamPolicies represents the list of IAM policies to be attached to the IAM role | | |
#### IamRole
IamRole represents the IAM role configuration
_Appears in:_
- [IRSASpec](#irsaspec)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `create` _boolean_ | Create specifies whether to create the IAM role or not | | |
| `name` _string_ | Name represents the name of the IAM role | | |
#### S3Discovery
+1 -1
View File
@@ -7,4 +7,4 @@ processor:
- "TypeMeta$"
render:
kubernetesVersion: 1.28
kubernetesVersion: 1.29
+62
View File
@@ -0,0 +1,62 @@
/*
Copyright 2024.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controller
import (
"context"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
irsav1alpha1 "github.com/kkb0318/irsa-manager/api/v1alpha1"
)
// IRSAReconciler reconciles a IRSA object
type IRSAReconciler struct {
client.Client
Scheme *runtime.Scheme
}
//+kubebuilder:rbac:groups=irsa.kkb0318.github.io,resources=irsas,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=irsa.kkb0318.github.io,resources=irsas/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=irsa.kkb0318.github.io,resources=irsas/finalizers,verbs=update
// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the IRSA object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.16.3/pkg/reconcile
func (r *IRSAReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
_ = log.FromContext(ctx)
// TODO(user): your logic here
return ctrl.Result{}, nil
}
// SetupWithManager sets up the controller with the Manager.
func (r *IRSAReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&irsav1alpha1.IRSA{}).
Complete(r)
}
@@ -0,0 +1,84 @@
/*
Copyright 2024.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controller
import (
"context"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
irsav1alpha1 "github.com/kkb0318/irsa-manager/api/v1alpha1"
)
var _ = Describe("IRSA Controller", func() {
Context("When reconciling a resource", func() {
const resourceName = "test-resource"
ctx := context.Background()
typeNamespacedName := types.NamespacedName{
Name: resourceName,
Namespace: "default", // TODO(user):Modify as needed
}
irsa := &irsav1alpha1.IRSA{}
BeforeEach(func() {
By("creating the custom resource for the Kind IRSA")
err := k8sClient.Get(ctx, typeNamespacedName, irsa)
if err != nil && errors.IsNotFound(err) {
resource := &irsav1alpha1.IRSA{
ObjectMeta: metav1.ObjectMeta{
Name: resourceName,
Namespace: "default",
},
// TODO(user): Specify other spec details if needed.
}
Expect(k8sClient.Create(ctx, resource)).To(Succeed())
}
})
AfterEach(func() {
// TODO(user): Cleanup logic after each test, like removing the resource instance.
resource := &irsav1alpha1.IRSA{}
err := k8sClient.Get(ctx, typeNamespacedName, resource)
Expect(err).NotTo(HaveOccurred())
By("Cleanup the specific resource instance IRSA")
Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
})
It("should successfully reconcile the resource", func() {
By("Reconciling the created resource")
controllerReconciler := &IRSAReconciler{
Client: k8sClient,
Scheme: k8sClient.Scheme(),
}
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
NamespacedName: typeNamespacedName,
})
Expect(err).NotTo(HaveOccurred())
// TODO(user): Add more specific assertions depending on your controller's reconciliation logic.
// Example: If you expect a certain status condition after reconciliation, verify it here.
})
})
})