Skip to content

Commit

Permalink
feat: create jobs from cronjobs
Browse files Browse the repository at this point in the history
  • Loading branch information
JGiola authored Oct 5, 2023
1 parent 707536b commit f409f5d
Show file tree
Hide file tree
Showing 9 changed files with 251 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- marketplace get command
- environment list command
- runtime list resources command
- runtime create job command
- events command
- version command

Expand Down
24 changes: 22 additions & 2 deletions docs/30_commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ This section explores a summary of the `miactl` commands and their functionaliti

:::tip

You can also display a complete help message on the command line by using the `--help` flag postponed to any `miactl` command or subcommand.

You can also display a complete help message on the command line by using the `--help` flag postponed to any `miactl`
command or subcommand.
This way you can also be sure of the available features of the `miactl` version you currently have installed.

:::
Expand Down Expand Up @@ -251,6 +251,26 @@ Available flags for the command:
- `--company-id`, to set the ID of the desired Company
- `--project-id`, to set the ID of the desired Project

### create job

The `runtime create job` subcommand allows you to manually create a job from a cronjob .

Usage:

```sh
miactl runtime create job ENVIRONMENT [flags]
```

Available flags for the command:

- `--from`, to set the cronjob name from which the job will be created
- `--endpoint`, to set the Console endpoint (default is `https://console.cloud.mia-platform.eu`)
- `--certificate-authority`, to provide the path to a custom CA certificate
- `--insecure-skip-tls-verify`, to disallow the check the validity of the certificate of the remote endpoint
- `--context`, to specify a different context from the currently selected one
- `--company-id`, to set the ID of the desired Company
- `--project-id`, to set the ID of the desired Project

## marketplace

View and manage Marketplace items
Expand Down
6 changes: 6 additions & 0 deletions internal/clioptions/clioptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ type CLIOptions struct {
OutputPath string

MarketplaceResourcePaths []string

FromCronJob string
}

// NewCLIOptions return a new CLIOptions instance
Expand Down Expand Up @@ -126,6 +128,10 @@ func (o *CLIOptions) AddMarketplaceApplyFlags(cmd *cobra.Command) {
}
}

func (o *CLIOptions) AddCreateJobFlags(flags *pflag.FlagSet) {
flags.StringVarP(&o.FromCronJob, "from", "", "", "The name of the cronjob to create a Job from")
}

func (o *CLIOptions) ToRESTConfig() (*client.Config, error) {
locator := cliconfig.NewConfigPathLocator()
locator.ExplicitPath = o.MiactlConfig
Expand Down
111 changes: 111 additions & 0 deletions internal/cmd/resources/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright Mia srl
// SPDX-License-Identifier: Apache-2.0
//
// 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 resources

import (
"context"
"encoding/json"
"fmt"

"github.com/mia-platform/miactl/internal/client"
"github.com/mia-platform/miactl/internal/clioptions"
"github.com/mia-platform/miactl/internal/resources"
"github.com/spf13/cobra"
)

const (
createJobTemplate = "/api/projects/%s/environments/%s/jobs/"
)

func CreateCommand(options *clioptions.CLIOptions) *cobra.Command {
cmd := &cobra.Command{
Use: "create",
Short: "Create Mia-Platform Console runtime resources",
Long: "Create Mia-Platform Console runtime resources.",
}

// add cmd flags

// add sub commands
cmd.AddCommand(
jobCommand(options),
)

return cmd
}

func jobCommand(options *clioptions.CLIOptions) *cobra.Command {
cmd := &cobra.Command{
Use: "job ENVIRONMENT",
Short: "Create a job from a cronjob in the selected environment and project",
Long: "Create a job from a cronjob in the selected environment and project",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
restConfig, err := options.ToRESTConfig()
cobra.CheckErr(err)
client, err := client.APIClientForConfig(restConfig)
cobra.CheckErr(err)
return createJob(client, restConfig.ProjectID, args[0], options.FromCronJob)
},
}

// add cmd flags
options.AddCreateJobFlags(cmd.Flags())
if err := cmd.MarkFlagRequired("from"); err != nil {
// programming error, panic and broke everything
panic(err)
}

return cmd
}

func createJob(client *client.APIClient, projectID, environment, cronjobName string) error {
if projectID == "" {
return fmt.Errorf("missing project id, please set one with the flag or context")
}

requestBody := &resources.CreateJobRequest{
From: "cronjob",
ResourceName: cronjobName,
}

bodyBytes, err := json.Marshal(requestBody)
if err != nil {
return err
}

response, err := client.
Post().
APIPath(fmt.Sprintf(createJobTemplate, projectID, environment)).
Body(bodyBytes).
Do(context.Background())

if err != nil {
return err
}

if err := response.Error(); err != nil {
return err
}

var createResponse resources.CreateJob
if err := response.ParseResponse(&createResponse); err != nil {
return err
}

fmt.Printf("Job %s create successfully!\n", createResponse.JobName)
return nil
}
94 changes: 94 additions & 0 deletions internal/cmd/resources/create_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright Mia srl
// SPDX-License-Identifier: Apache-2.0
//
// 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 resources

import (
"fmt"
"net/http"
"net/http/httptest"
"testing"

"github.com/mia-platform/miactl/internal/client"
"github.com/mia-platform/miactl/internal/resources"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestCreateJob(t *testing.T) {
testCases := map[string]struct {
testServer *httptest.Server
projectID string
err bool
}{
"create job end with success": {
testServer: createJobTestServer(t),
projectID: "success",
},
"create job end with error": {
testServer: createJobTestServer(t),
projectID: "fail",
err: true,
},
}

for testName, testCase := range testCases {
t.Run(testName, func(t *testing.T) {
server := testCase.testServer
defer server.Close()

client, err := client.APIClientForConfig(&client.Config{
Host: server.URL,
})
require.NoError(t, err)

err = createJob(client, testCase.projectID, "env-id", "cronjob-name")
if testCase.err {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
}

func createJobTestServer(t *testing.T) *httptest.Server {
t.Helper()
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == http.MethodPost && r.URL.Path == fmt.Sprintf(createJobTemplate, "success", "env-id"):
response := resources.CreateJob{
JobName: "new-job-name",
}
data, err := resources.EncodeResourceToJSON(response)
assert.NoError(t, err)
w.WriteHeader(http.StatusOK)
w.Write(data)
case r.Method == http.MethodPost && r.URL.Path == fmt.Sprintf(createJobTemplate, "fail", "env-id"):
response := resources.APIError{
StatusCode: http.StatusInternalServerError,
Message: "Error creating job",
}
data, err := resources.EncodeResourceToJSON(response)
assert.NoError(t, err)
w.WriteHeader(http.StatusInternalServerError)
w.Write(data)
default:
w.WriteHeader(http.StatusNotFound)
assert.Failf(t, "unexpected http call", "received call with method: %s uri %s", r.Method, r.RequestURI)
}
}))
return server
}
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -36,37 +36,37 @@ func TestPrintServicesList(t *testing.T) {
err bool
}{
"list services with success": {
testServer: testServer(t),
testServer: listResourceTestServer(t),
projectID: "found",
resourceType: ServicesResourceType,
},
"list deployments with success": {
testServer: testServer(t),
testServer: listResourceTestServer(t),
projectID: "found",
resourceType: DeploymentsResourceType,
},
"list pods with success": {
testServer: testServer(t),
testServer: listResourceTestServer(t),
projectID: "found",
resourceType: PodsResourceType,
},
"list cronjobs with success": {
testServer: testServer(t),
testServer: listResourceTestServer(t),
projectID: "found",
resourceType: CronJobsResourceType,
},
"list jobs with success": {
testServer: testServer(t),
testServer: listResourceTestServer(t),
projectID: "found",
resourceType: JobsResourceType,
},
"list deployments with empty response": {
testServer: testServer(t),
testServer: listResourceTestServer(t),
projectID: "empty",
resourceType: DeploymentResourceType,
},
"failed request": {
testServer: testServer(t),
testServer: listResourceTestServer(t),
projectID: "fail",
err: true,
resourceType: PodsResourceType,
Expand All @@ -93,7 +93,7 @@ func TestPrintServicesList(t *testing.T) {
}
}

func testServer(t *testing.T) *httptest.Server {
func listResourceTestServer(t *testing.T) *httptest.Server {
t.Helper()
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
Expand Down
5 changes: 5 additions & 0 deletions internal/resources/requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,8 @@ type DeployProjectRequest struct {
Type string `json:"deployType"` //nolint: tagliatelle
ForceDeploy bool `json:"forceDeplpuWhenNoSemver"` //nolint: tagliatelle
}

type CreateJobRequest struct {
From string `json:"from"`
ResourceName string `json:"resourceName"`
}
4 changes: 4 additions & 0 deletions internal/resources/responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ type PipelineStatus struct {
Status string `json:"status"`
}

type CreateJob struct {
JobName string `json:"jobName"`
}

type ServiceAccount struct {
ClientID string `json:"clientId"`
ClientSecret string `json:"clientSecret"`
Expand Down

0 comments on commit f409f5d

Please sign in to comment.