Skip to content

Commit 245d50c

Browse files
committed
remove other commits, and add test files missing
Signed-off-by: Filinto Duran <[email protected]>
1 parent dbbe022 commit 245d50c

File tree

8 files changed

+245
-7
lines changed

8 files changed

+245
-7
lines changed

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ require (
253253
k8s.io/kubectl v0.26.0 // indirect
254254
k8s.io/utils v0.0.0-20240102154912-e7106e64919e // indirect
255255
oras.land/oras-go v1.2.2 // indirect
256-
sigs.k8s.io/controller-runtime v0.16.3 // indirect
256+
sigs.k8s.io/controller-runtime v0.16.3
257257
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
258258
sigs.k8s.io/kustomize/api v0.15.0 // indirect
259259
sigs.k8s.io/kustomize/kyaml v0.15.0 // indirect

pkg/kubernetes/client.go

+49
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@ limitations under the License.
1414
package kubernetes
1515

1616
import (
17+
"errors"
1718
"flag"
1819
"sync"
1920

21+
"k8s.io/apimachinery/pkg/runtime"
2022
k8s "k8s.io/client-go/kubernetes"
2123
"k8s.io/client-go/rest"
2224
"k8s.io/client-go/tools/clientcmd"
25+
"sigs.k8s.io/controller-runtime/pkg/client"
2326

2427
scheme "github.com/dapr/dapr/pkg/client/clientset/versioned"
2528

@@ -31,6 +34,14 @@ import (
3134

3235
// oidc auth
3336
_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
37+
38+
componentsapi "github.com/dapr/dapr/pkg/apis/components/v1alpha1"
39+
configurationapi "github.com/dapr/dapr/pkg/apis/configuration/v1alpha1"
40+
httpendpointsapi "github.com/dapr/dapr/pkg/apis/httpEndpoint/v1alpha1"
41+
resiliencyapi "github.com/dapr/dapr/pkg/apis/resiliency/v1alpha1"
42+
subscriptionsapiV1alpha1 "github.com/dapr/dapr/pkg/apis/subscriptions/v1alpha1"
43+
subapi "github.com/dapr/dapr/pkg/apis/subscriptions/v2alpha1"
44+
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
3445
)
3546

3647
var (
@@ -96,3 +107,41 @@ func DaprClient() (scheme.Interface, error) {
96107
}
97108
return scheme.NewForConfig(config)
98109
}
110+
111+
// buildScheme builds the scheme for the controller-runtime client
112+
// from https://github.com/dapr/dapr/blob/eb49e564fbd704ceb1379498fc8e94ad7110840f/pkg/operator/operator.go#L444-L466
113+
func buildScheme() (*runtime.Scheme, error) {
114+
builders := []func(*runtime.Scheme) error{
115+
clientgoscheme.AddToScheme,
116+
componentsapi.AddToScheme,
117+
configurationapi.AddToScheme,
118+
resiliencyapi.AddToScheme,
119+
httpendpointsapi.AddToScheme,
120+
subscriptionsapiV1alpha1.AddToScheme,
121+
subapi.AddToScheme,
122+
}
123+
124+
errs := make([]error, len(builders))
125+
scheme := runtime.NewScheme()
126+
for i, builder := range builders {
127+
errs[i] = builder(scheme)
128+
}
129+
130+
return scheme, errors.Join(errs...)
131+
}
132+
133+
// CtrlClient returns a new Controller-Runtime Client (https://pkg.go.dev/sigs.k8s.io/controller-runtime/pkg/client) - no caching
134+
// with the scheme built with the Dapr API groups.
135+
func CtrlClient() (client.Client, error) {
136+
config, err := getConfig()
137+
if err != nil {
138+
return nil, err
139+
}
140+
141+
scheme, err := buildScheme()
142+
if err != nil {
143+
return nil, err
144+
}
145+
146+
return client.New(config, client.Options{Scheme: scheme})
147+
}

pkg/kubernetes/resources.go

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package kubernetes
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
10+
k8serrors "k8s.io/apimachinery/pkg/api/errors"
11+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
12+
"k8s.io/apimachinery/pkg/runtime/serializer/yaml"
13+
"sigs.k8s.io/controller-runtime/pkg/client"
14+
15+
"github.com/dapr/cli/pkg/print"
16+
)
17+
18+
func getResources(resourcesFolder string) ([]client.Object, error) {
19+
// Create YAML decoder
20+
decUnstructured := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme)
21+
22+
// Read files from the resources folder
23+
files, err := os.ReadDir(resourcesFolder)
24+
if err != nil {
25+
return nil, fmt.Errorf("error reading resources folder: %w", err)
26+
}
27+
28+
var resources []client.Object
29+
for _, file := range files {
30+
if file.IsDir() || (!strings.HasSuffix(file.Name(), ".yaml") && !strings.HasSuffix(file.Name(), ".json")) {
31+
continue
32+
}
33+
34+
// Read file content
35+
content, err := os.ReadFile(filepath.Join(resourcesFolder, file.Name()))
36+
if err != nil {
37+
return nil, fmt.Errorf("error reading file %s: %w", file.Name(), err)
38+
}
39+
40+
// Decode YAML/JSON to unstructured
41+
obj := &unstructured.Unstructured{}
42+
_, _, err = decUnstructured.Decode(content, nil, obj)
43+
if err != nil {
44+
return nil, fmt.Errorf("error decoding file %s: %w", file.Name(), err)
45+
}
46+
47+
resources = append(resources, obj)
48+
}
49+
50+
return resources, nil
51+
}
52+
53+
func createOrUpdateResources(ctx context.Context, cl client.Client, resources []client.Object, namespace string) error {
54+
// create resources in k8s
55+
for _, resource := range resources {
56+
// clone the resource to avoid modifying the original
57+
obj := resource.DeepCopyObject().(*unstructured.Unstructured)
58+
// Set namespace on the resource metadata
59+
obj.SetNamespace(namespace)
60+
61+
print.InfoStatusEvent(os.Stdout, "Deploying resource %q kind %q to Kubernetes", obj.GetName(), obj.GetKind())
62+
63+
if err := cl.Create(ctx, obj); err != nil {
64+
if k8serrors.IsAlreadyExists(err) {
65+
print.InfoStatusEvent(os.Stdout, "Resource %q kind %q already exists, updating", obj.GetName(), obj.GetKind())
66+
if err := cl.Update(ctx, obj); err != nil {
67+
return err
68+
}
69+
} else {
70+
return fmt.Errorf("error deploying resource %q kind %q to Kubernetes: %w", obj.GetName(), obj.GetKind(), err)
71+
}
72+
}
73+
}
74+
return nil
75+
}

pkg/kubernetes/resources_test.go

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package kubernetes
2+
3+
import (
4+
"path/filepath"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestGetResources(t *testing.T) {
12+
tests := []struct {
13+
name string
14+
folder string
15+
expectError bool
16+
expectedCount int
17+
expectedResourceKinds []string
18+
}{
19+
{
20+
name: "resources from testdata",
21+
folder: filepath.Join("testdata", "resources"),
22+
expectError: false,
23+
expectedCount: 3,
24+
expectedResourceKinds: []string{"Component", "Configuration", "Resiliency"},
25+
},
26+
{
27+
name: "non-existent folder",
28+
folder: filepath.Join("testdata", "non-existent"),
29+
expectError: true,
30+
},
31+
}
32+
33+
for _, tt := range tests {
34+
t.Run(tt.name, func(t *testing.T) {
35+
resources, err := getResources(tt.folder)
36+
if tt.expectError {
37+
assert.Error(t, err)
38+
return
39+
}
40+
41+
require.NoError(t, err)
42+
assert.Len(t, resources, tt.expectedCount)
43+
foundKinds := []string{}
44+
for _, resource := range resources {
45+
foundKinds = append(foundKinds, resource.GetObjectKind().GroupVersionKind().Kind)
46+
}
47+
assert.ElementsMatch(t, tt.expectedResourceKinds, foundKinds)
48+
})
49+
}
50+
}

pkg/kubernetes/run.go

+19-6
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,23 @@ func Run(runFilePath string, config runfileconfig.RunFileConfig) (bool, error) {
119119
runStates := []runState{}
120120
print.InfoStatusEvent(os.Stdout, "This is a preview feature and subject to change in future releases.")
121121

122+
ctrlClient, cErr := CtrlClient()
123+
if cErr != nil {
124+
// exit with error.
125+
return true, fmt.Errorf("error getting controller-runtime k8s client: %w", cErr)
126+
}
127+
128+
resources, err := getResources(config.Common.ResourcesPath)
129+
if err != nil {
130+
print.FailureStatusEvent(os.Stderr, "Error getting resources from %q: %s", config.Common.ResourcesPath, err.Error())
131+
exitWithError = true
132+
}
133+
134+
if err := createOrUpdateResources(context.Background(), ctrlClient, resources, namespace); err != nil {
135+
print.FailureStatusEvent(os.Stderr, "Error creating or updating resources: %s", err.Error())
136+
exitWithError = true
137+
}
138+
122139
for _, app := range config.Apps {
123140
print.StatusEvent(os.Stdout, print.LogInfo, "Validating config and starting app %q", app.RunConfig.AppID)
124141
// Set defaults if zero value provided in config yaml.
@@ -140,11 +157,7 @@ func Run(runFilePath string, config runfileconfig.RunFileConfig) (bool, error) {
140157

141158
// create default deployment config.
142159
dep := createDeploymentConfig(daprClient, app)
143-
if err != nil {
144-
print.FailureStatusEvent(os.Stderr, "Error creating deployment file for app %q present in %s: %s", app.RunConfig.AppID, runFilePath, err.Error())
145-
exitWithError = true
146-
break
147-
}
160+
148161
// overwrite <app-id>/.dapr/deploy/service.yaml.
149162
// overwrite <app-id>/.dapr/deploy/deployment.yaml.
150163

@@ -297,7 +310,7 @@ func createDeploymentConfig(client versioned.Interface, app runfileconfig.App) d
297310
Name: app.AppID,
298311
Image: app.ContainerImage,
299312
Env: getEnv(app),
300-
ImagePullPolicy: corev1.PullAlways,
313+
ImagePullPolicy: corev1.PullPolicy(app.ContainerImagePullPolicy),
301314
},
302315
},
303316
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
apiVersion: dapr.io/v1alpha1
2+
kind: Configuration
3+
metadata:
4+
name: daprConfig
5+
namespace: default
6+
spec:
7+
tracing:
8+
samplingRate: "1"
9+
zipkin:
10+
endpointAddress: "http://localhost:9411/api/v2/spans"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
apiVersion: dapr.io/v1alpha1
2+
kind: Resiliency
3+
metadata:
4+
name: myresiliency
5+
scopes:
6+
- checkout
7+
8+
spec:
9+
policies:
10+
retries:
11+
retryForever:
12+
policy: constant
13+
duration: 5s
14+
maxRetries: -1
15+
16+
circuitBreakers:
17+
simpleCB:
18+
maxRequests: 1
19+
timeout: 5s
20+
trip: consecutiveFailures >= 5
21+
22+
targets:
23+
apps:
24+
order-processor:
25+
retry: retryForever
26+
circuitBreaker: simpleCB
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
apiVersion: dapr.io/v1alpha1
2+
kind: Component
3+
metadata:
4+
name: statestore
5+
spec:
6+
type: state.redis
7+
version: v1
8+
initTimeout: 1m
9+
metadata:
10+
- name: redisHost
11+
value: localhost:6379
12+
- name: redisPassword
13+
value: ""
14+
- name: actorStateStore
15+
value: "true"

0 commit comments

Comments
 (0)