diff --git a/cmd/zrok/release.go b/cmd/zrok/release.go index aad4372b..e4dc9599 100644 --- a/cmd/zrok/release.go +++ b/cmd/zrok/release.go @@ -56,7 +56,10 @@ func (cmd *releaseCommand) run(_ *cobra.Command, args []string) { Reserved: true, } if _, err := zrok.Service.Unshare(req, auth); err != nil { - logrus.Errorf("error releasing service '%v': %v", svcToken, err) + if !panicInstead { + showError("error releasing service", err) + } + panic(err) } logrus.Infof("reserved service '%v' released", svcToken) diff --git a/cmd/zrok/reserve.go b/cmd/zrok/reserve.go index aae572e0..509bb090 100644 --- a/cmd/zrok/reserve.go +++ b/cmd/zrok/reserve.go @@ -41,7 +41,7 @@ func (cmd *reserveCommand) run(_ *cobra.Command, args []string) { showError("invalid sharing mode; expecting 'public' or 'private'", nil) } - targetEndpoint, err := url.Parse(args[0]) + targetEndpoint, err := url.Parse(args[1]) if err != nil { if !panicInstead { showError("invalid target endpoint URL", err) diff --git a/cmd/zrok/share_reserved.go b/cmd/zrok/share_reserved.go new file mode 100644 index 00000000..5fe0c38a --- /dev/null +++ b/cmd/zrok/share_reserved.go @@ -0,0 +1,129 @@ +package main + +import ( + ui "github.com/gizak/termui/v3" + httptransport "github.com/go-openapi/runtime/client" + "github.com/openziti-test-kitchen/zrok/endpoints/backend" + "github.com/openziti-test-kitchen/zrok/rest_client_zrok/service" + "github.com/openziti-test-kitchen/zrok/rest_model_zrok" + "github.com/openziti-test-kitchen/zrok/zrokdir" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "net/url" + "time" +) + +func init() { + shareCmd.AddCommand(newShareReservedCommand().cmd) +} + +type shareReservedCommand struct { + overrideEndpoint string + cmd *cobra.Command +} + +func newShareReservedCommand() *shareReservedCommand { + cmd := &cobra.Command{ + Use: "reserved ", + } + command := &shareReservedCommand{cmd: cmd} + cmd.Flags().StringVar(&command.overrideEndpoint, "override-endpoint", "", "Override the stored target endpoint with a replacement") + cmd.Run = command.run + return command +} + +func (cmd *shareReservedCommand) run(_ *cobra.Command, args []string) { + svcToken := args[0] + targetEndpoint := "" + if cmd.overrideEndpoint != "" { + e, err := url.Parse(cmd.overrideEndpoint) + if err != nil { + if !panicInstead { + showError("invalid override endpoint URL", err) + } + panic(err) + } + if e.Scheme == "" { + e.Scheme = "https" + } + targetEndpoint = e.String() + } + + env, err := zrokdir.LoadEnvironment() + if err != nil { + ui.Close() + if !panicInstead { + showError("unable to load environment; did you 'zrok enable'?", err) + } + panic(err) + } + zrok, err := zrokdir.ZrokClient(env.ApiEndpoint) + if err != nil { + ui.Close() + if !panicInstead { + showError("unable to create zrok client", err) + } + panic(err) + } + auth := httptransport.APIKeyAuth("X-TOKEN", "header", env.Token) + req := service.NewGetServiceParams() + req.Body = &rest_model_zrok.ServiceRequest{ + EnvZID: env.ZId, + SvcToken: svcToken, + } + resp, err := zrok.Service.GetService(req, auth) + if err != nil { + if !panicInstead { + showError("unable to retrieve reserved service", err) + } + panic(err) + } + if targetEndpoint == "" { + targetEndpoint = resp.Payload.BackendProxyEndpoint + } + + zif, err := zrokdir.ZitiIdentityFile("backend") + if err != nil { + ui.Close() + if !panicInstead { + showError("unable to load ziti identity configuration", err) + } + panic(err) + } + cfg := &backend.Config{ + IdentityPath: zif, + EndpointAddress: targetEndpoint, + Service: svcToken, + } + logrus.Infof("sharing target endpoint: '%v'", cfg.EndpointAddress) + + httpProxy, err := backend.NewHTTP(cfg) + if err != nil { + ui.Close() + if !panicInstead { + showError("unable to create http backend", err) + } + panic(err) + } + + go func() { + if err := httpProxy.Run(); err != nil { + if !panicInstead { + showError("unable to run http proxy", err) + } + panic(err) + } + }() + + switch resp.Payload.ShareMode { + case "public": + logrus.Infof("access your zrok service: %v", resp.Payload.FrontendEndpoint) + + case "private": + logrus.Infof("use this command to access your zrok service: 'zrok access private %v'", svcToken) + } + + for { + time.Sleep(30 * time.Second) + } +} diff --git a/controller/controller.go b/controller/controller.go index ad85849f..9919aac1 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -8,6 +8,7 @@ import ( "github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations" "github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/account" "github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/metadata" + "github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/service" "github.com/pkg/errors" ) @@ -35,6 +36,7 @@ func Run(inCfg *Config) error { api.MetadataOverviewHandler = metadata.OverviewHandlerFunc(overviewHandler) api.MetadataVersionHandler = metadata.VersionHandlerFunc(versionHandler) api.ServiceAccessHandler = newAccessHandler() + api.ServiceGetServiceHandler = service.GetServiceHandlerFunc(getServiceHandler) api.ServiceShareHandler = newShareHandler() api.ServiceUnaccessHandler = newUnaccessHandler() api.ServiceUnshareHandler = newUnshareHandler() diff --git a/controller/get_service.go b/controller/get_service.go new file mode 100644 index 00000000..b4a3b251 --- /dev/null +++ b/controller/get_service.go @@ -0,0 +1,61 @@ +package controller + +import ( + "github.com/go-openapi/runtime/middleware" + "github.com/openziti-test-kitchen/zrok/rest_model_zrok" + "github.com/openziti-test-kitchen/zrok/rest_server_zrok/operations/service" + "github.com/sirupsen/logrus" +) + +func getServiceHandler(params service.GetServiceParams, principal *rest_model_zrok.Principal) middleware.Responder { + envZId := params.Body.EnvZID + svcToken := params.Body.SvcToken + + tx, err := str.Begin() + if err != nil { + logrus.Errorf("error starting transaction: %v", err) + return service.NewGetServiceInternalServerError() + } + defer func() { _ = tx.Rollback() }() + + ssvc, err := str.FindServiceWithToken(svcToken, tx) + if err != nil { + logrus.Errorf("error finding service with token '%v': %v", svcToken, err) + return service.NewGetServiceNotFound() + } + senvs, err := str.FindEnvironmentsForAccount(int(principal.ID), tx) + if err != nil { + logrus.Errorf("error listing environments for account '%v': %v", principal.Email, err) + return service.NewGetServiceInternalServerError() + } + envFound := false + for _, senv := range senvs { + if senv.Id == ssvc.EnvironmentId && senv.ZId == envZId { + envFound = true + break + } + } + if !envFound { + logrus.Errorf("service '%v' not in environment '%v'", svcToken, envZId) + return service.NewGetServiceNotFound() + } + + svc := &rest_model_zrok.Service03{ + Token: ssvc.Token, + ZID: ssvc.ZId, + ShareMode: ssvc.ShareMode, + BackendMode: ssvc.BackendMode, + Reserved: ssvc.Reserved, + } + if ssvc.FrontendSelection != nil { + svc.FrontendSelection = *ssvc.FrontendSelection + } + if ssvc.FrontendEndpoint != nil { + svc.FrontendEndpoint = *ssvc.FrontendEndpoint + } + if ssvc.BackendProxyEndpoint != nil { + svc.BackendProxyEndpoint = *ssvc.BackendProxyEndpoint + } + + return service.NewGetServiceOK().WithPayload(svc) +} diff --git a/controller/store/frontend.go b/controller/store/frontend.go index 306343eb..8fa883eb 100644 --- a/controller/store/frontend.go +++ b/controller/store/frontend.go @@ -34,9 +34,9 @@ func (str *Store) GetFrontend(id int, tx *sqlx.Tx) (*Frontend, error) { return i, nil } -func (str *Store) FindFrontendNamed(name string, tx *sqlx.Tx) (*Frontend, error) { +func (str *Store) FindFrontendWithToken(token string, tx *sqlx.Tx) (*Frontend, error) { i := &Frontend{} - if err := tx.QueryRowx("select frontends.* from frontends where name = $1", name).StructScan(i); err != nil { + if err := tx.QueryRowx("select frontends.* from frontends where token = $1", token).StructScan(i); err != nil { return nil, errors.Wrap(err, "error selecting frontend by name") } return i, nil diff --git a/controller/store/frontend_test.go b/controller/store/frontend_test.go index 53344a42..24aa191c 100644 --- a/controller/store/frontend_test.go +++ b/controller/store/frontend_test.go @@ -51,7 +51,7 @@ func TestPublicFrontend(t *testing.T) { err = str.DeleteFrontend(fe.Id, tx) assert.Nil(t, err) - fe0, err = str.FindFrontendNamed(feName, tx) + fe0, err = str.FindFrontendWithToken(feName, tx) assert.NotNil(t, err) assert.Nil(t, fe0) diff --git a/controller/store/sql/postgresql/002_v0_3_0_frontend_selection.sql b/controller/store/sql/postgresql/002_v0_3_0_frontend_selection.sql index 6e4ccee3..ff6d5158 100644 --- a/controller/store/sql/postgresql/002_v0_3_0_frontend_selection.sql +++ b/controller/store/sql/postgresql/002_v0_3_0_frontend_selection.sql @@ -4,7 +4,7 @@ create table frontends ( id serial primary key, environment_id integer not null references environments(id), token varchar(32) not null unique, - z_id varchar(32) not null unique, + z_id varchar(32) not null, public_name varchar(64) unique, reserved boolean not null default(false), created_at timestamptz not null default(current_timestamp), diff --git a/controller/store/sql/sqlite3/002_v0_3_0_frontend_selection.sql b/controller/store/sql/sqlite3/002_v0_3_0_frontend_selection.sql index 613e0158..d8dc53e1 100644 --- a/controller/store/sql/sqlite3/002_v0_3_0_frontend_selection.sql +++ b/controller/store/sql/sqlite3/002_v0_3_0_frontend_selection.sql @@ -21,7 +21,7 @@ create table frontends ( id integer primary key, environment_id integer not null references environments(id), token varchar(32) not null unique, - z_id varchar(32) not null unique, + z_id varchar(32) not null, public_name varchar(64) unique, reserved boolean not null default(false), created_at datetime not null default(strftime('%Y-%m-%d %H:%M:%f', 'now')), diff --git a/controller/unaccess.go b/controller/unaccess.go index 4ce2625b..1f30d353 100644 --- a/controller/unaccess.go +++ b/controller/unaccess.go @@ -53,7 +53,7 @@ func (h *unaccessHandler) Handle(params service.UnaccessParams, principal *rest_ return service.NewUnaccessUnauthorized() } - sfe, err := str.FindFrontendNamed(feToken, tx) + sfe, err := str.FindFrontendWithToken(feToken, tx) if err != nil { logrus.Error(err) return service.NewUnaccessInternalServerError() diff --git a/controller/unshare.go b/controller/unshare.go index 4a02ebde..8b3fdd19 100644 --- a/controller/unshare.go +++ b/controller/unshare.go @@ -37,7 +37,7 @@ func (h *unshareHandler) Handle(params service.UnshareParams, principal *rest_mo svcZId, err := h.findServiceZId(svcToken, edge) if err != nil { logrus.Error(err) - return service.NewUnshareInternalServerError() + return service.NewUnshareNotFound() } var senv *store.Environment if envs, err := str.FindEnvironmentsForAccount(int(principal.ID), tx); err == nil { @@ -54,7 +54,7 @@ func (h *unshareHandler) Handle(params service.UnshareParams, principal *rest_mo } } else { logrus.Errorf("error finding environments for account '%v': %v", principal.Email, err) - return service.NewUnshareInternalServerError() + return service.NewUnshareNotFound() } var ssvc *store.Service diff --git a/rest_client_zrok/service/get_service_responses.go b/rest_client_zrok/service/get_service_responses.go index c41aedf4..670ad85e 100644 --- a/rest_client_zrok/service/get_service_responses.go +++ b/rest_client_zrok/service/get_service_responses.go @@ -63,7 +63,7 @@ GetServiceOK describes a response with status code 200, with default header valu ok */ type GetServiceOK struct { - Payload *rest_model_zrok.Service + Payload *rest_model_zrok.Service03 } // IsSuccess returns true when this get service o k response has a 2xx status code @@ -99,13 +99,13 @@ func (o *GetServiceOK) String() string { return fmt.Sprintf("[GET /service][%d] getServiceOK %+v", 200, o.Payload) } -func (o *GetServiceOK) GetPayload() *rest_model_zrok.Service { +func (o *GetServiceOK) GetPayload() *rest_model_zrok.Service03 { return o.Payload } func (o *GetServiceOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error { - o.Payload = new(rest_model_zrok.Service) + o.Payload = new(rest_model_zrok.Service03) // response payload if err := consumer.Consume(response.Body(), o.Payload); err != nil && err != io.EOF { diff --git a/rest_model_zrok/service_request.go b/rest_model_zrok/service_request.go index 8eaba697..e3520dd3 100644 --- a/rest_model_zrok/service_request.go +++ b/rest_model_zrok/service_request.go @@ -17,6 +17,9 @@ import ( // swagger:model serviceRequest type ServiceRequest struct { + // env z Id + EnvZID string `json:"envZId,omitempty"` + // svc token SvcToken string `json:"svcToken,omitempty"` } diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index e994cad0..d3254355 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -284,7 +284,7 @@ func init() { "200": { "description": "ok", "schema": { - "$ref": "#/definitions/service" + "$ref": "#/definitions/service03" } }, "401": { @@ -693,6 +693,9 @@ func init() { "serviceRequest": { "type": "object", "properties": { + "envZId": { + "type": "string" + }, "svcToken": { "type": "string" } @@ -1082,7 +1085,7 @@ func init() { "200": { "description": "ok", "schema": { - "$ref": "#/definitions/service" + "$ref": "#/definitions/service03" } }, "401": { @@ -1491,6 +1494,9 @@ func init() { "serviceRequest": { "type": "object", "properties": { + "envZId": { + "type": "string" + }, "svcToken": { "type": "string" } diff --git a/rest_server_zrok/operations/service/get_service_responses.go b/rest_server_zrok/operations/service/get_service_responses.go index 5de48837..738bd5bf 100644 --- a/rest_server_zrok/operations/service/get_service_responses.go +++ b/rest_server_zrok/operations/service/get_service_responses.go @@ -26,7 +26,7 @@ type GetServiceOK struct { /* In: Body */ - Payload *rest_model_zrok.Service `json:"body,omitempty"` + Payload *rest_model_zrok.Service03 `json:"body,omitempty"` } // NewGetServiceOK creates GetServiceOK with default headers values @@ -36,13 +36,13 @@ func NewGetServiceOK() *GetServiceOK { } // WithPayload adds the payload to the get service o k response -func (o *GetServiceOK) WithPayload(payload *rest_model_zrok.Service) *GetServiceOK { +func (o *GetServiceOK) WithPayload(payload *rest_model_zrok.Service03) *GetServiceOK { o.Payload = payload return o } // SetPayload sets the payload to the get service o k response -func (o *GetServiceOK) SetPayload(payload *rest_model_zrok.Service) { +func (o *GetServiceOK) SetPayload(payload *rest_model_zrok.Service03) { o.Payload = payload } diff --git a/specs/zrok.yml b/specs/zrok.yml index b29fe825..c80ac9b8 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -211,7 +211,7 @@ paths: 200: description: ok schema: - $ref: "#/definitions/service" + $ref: "#/definitions/service03" 401: description: unauthorized 404: @@ -465,6 +465,8 @@ definitions: serviceRequest: type: object properties: + envZId: + type: string svcToken: type: string diff --git a/ui/src/api/service.js b/ui/src/api/service.js index a3e9a0f3..d60ea918 100644 --- a/ui/src/api/service.js +++ b/ui/src/api/service.js @@ -20,7 +20,7 @@ export function access(options) { /** * @param {object} options Optional options * @param {module:types.serviceRequest} [options.body] - * @return {Promise} ok + * @return {Promise} ok */ export function getService(options) { if (!options) options = {} diff --git a/ui/src/api/types.js b/ui/src/api/types.js index f2907b42..8678bf2c 100644 --- a/ui/src/api/types.js +++ b/ui/src/api/types.js @@ -138,6 +138,7 @@ * @typedef serviceRequest * @memberof module:types * + * @property {string} envZId * @property {string} svcToken */