Files

187 lines
6.2 KiB
Go

package controller
import (
"github.com/go-openapi/runtime/middleware"
"github.com/jmoiron/sqlx"
"github.com/michaelquigley/df/dl"
"github.com/openziti/zrok/v2/controller/automation"
"github.com/openziti/zrok/v2/controller/store"
"github.com/openziti/zrok/v2/rest_model_zrok"
"github.com/openziti/zrok/v2/rest_server_zrok/operations/share"
"github.com/openziti/zrok/v2/util"
"github.com/pkg/errors"
)
type unshareHandler struct{}
func newUnshareHandler() *unshareHandler {
return &unshareHandler{}
}
func (h *unshareHandler) Handle(params share.UnshareParams, principal *rest_model_zrok.Principal) middleware.Responder {
trx, err := str.Begin()
if err != nil {
dl.Errorf("error starting transaction for '%v': %v", principal.Email, err)
return share.NewUnshareInternalServerError()
}
defer func() { _ = trx.Rollback() }()
shrToken := params.Body.ShareToken
envZId := params.Body.EnvZID
// validate environment
env, err := h.validateEnvironment(envZId, principal, trx)
if err != nil {
dl.Errorf("environment validation failed for '%v': %v", principal.Email, err)
return share.NewUnshareNotFound()
}
// find and validate share
shr, err := h.findAndValidateShare(shrToken, env, trx)
if err != nil {
dl.Errorf("share validation failed for '%v': %v", principal.Email, err)
return share.NewUnshareNotFound()
}
// deallocate ziti resources using automation framework
if err := h.deallocateResources(shrToken); err != nil {
dl.Warnf("error deallocating ziti resources for share '%v': %v", shrToken, err)
}
// send unbind mapping updates before cleaning up share name mappings
if err := h.processDynamicMappings(shr.Id, trx); err != nil {
dl.Errorf("error sending unbind mapping updates for '%v': %v", shrToken, err)
}
// clean up share name mappings
if err := h.cleanupShareNameMappings(shr.Id, trx); err != nil {
dl.Errorf("error cleaning up share name mappings for '%v': %v", shrToken, err)
return share.NewUnshareInternalServerError()
}
// clean up access grants
if err := str.DeleteAccessGrantsForShare(shr.Id, trx); err != nil {
dl.Errorf("error deleting access grants for share '%v': %v", shrToken, err)
return share.NewUnshareInternalServerError()
}
// delete the share record
if err := str.DeleteShare(shr.Id, trx); err != nil {
dl.Errorf("error deleting share '%v': %v", shrToken, err)
return share.NewUnshareInternalServerError()
}
// commit transaction
if err := trx.Commit(); err != nil {
dl.Errorf("error committing transaction for '%v': %v", shrToken, err)
return share.NewUnshareInternalServerError()
}
dl.Infof("successfully unshared '%v' for '%v'", shrToken, principal.Email)
return share.NewUnshareOK()
}
func (h *unshareHandler) validateEnvironment(envZId string, principal *rest_model_zrok.Principal, trx *sqlx.Tx) (*store.Environment, error) {
env, err := str.FindEnvironmentForAccount(envZId, int(principal.ID), trx)
if err != nil {
return nil, errors.Wrapf(err, "error finding environment '%v' for account '%v'", envZId, principal.Email)
}
return env, nil
}
func (h *unshareHandler) findAndValidateShare(shrToken string, env *store.Environment, trx *sqlx.Tx) (*store.Share, error) {
shares, err := str.FindSharesForEnvironment(env.Id, trx)
if err != nil {
return nil, errors.Wrapf(err, "error finding shares for environment '%v'", env.ZId)
}
for _, share := range shares {
if share.Token == shrToken {
return share, nil
}
}
return nil, errors.Errorf("share '%v' not found in environment '%v'", shrToken, env.ZId)
}
func (h *unshareHandler) deallocateResources(shrToken string) error {
// get shared automation client
za, err := automation.NewZitiAutomation(cfg.Ziti)
if err != nil {
return errors.Wrap(err, "error getting ziti automation client")
}
// use fluent workflow API for tag-based cleanup
err = za.CleanupByTag("zrokShareToken", shrToken)
if err != nil {
return errors.Wrapf(err, "error cleaning up ziti resources for share '%v'", shrToken)
}
dl.Infof("deallocated ziti resources for share '%v'", shrToken)
return nil
}
func (h *unshareHandler) cleanupShareNameMappings(shareId int, trx *sqlx.Tx) error {
details, err := str.FindShareNameCleanupDetailsByShareId(shareId, trx)
if err != nil {
return errors.Wrapf(err, "error finding share name cleanup details for share '%v'", shareId)
}
for _, detail := range details {
// only delete names that are not reserved and are not already deleted
if !detail.Reserved && !detail.NameDeleted {
if err := str.DeleteName(detail.NameId, trx); err != nil {
return errors.Wrapf(err, "error deleting dynamically allocated name '%v'", detail.Name)
}
dl.Debugf("deleted dynamically allocated name '%v'", detail.Name)
}
// delete the share name mapping
if err := str.DeleteShareNameMapping(detail.MappingId, trx); err != nil {
return errors.Wrapf(err, "error deleting share name mapping '%v'", detail.MappingId)
}
}
return nil
}
func (h *unshareHandler) processDynamicMappings(shareId int, trx *sqlx.Tx) error {
// only send updates if dynamic proxy controller is enabled
if dPCtrl == nil {
return nil
}
details, err := str.FindShareNameCleanupDetailsByShareId(shareId, trx)
if err != nil {
return errors.Wrapf(err, "error finding share name cleanup details for share '%v'", shareId)
}
for _, detail := range details {
if detail.NamespaceDeleted {
dl.Warnf("namespace '%v' is deleted while unbinding share name mapping '%v'", detail.NamespaceName, detail.MappingId)
continue
}
// find dynamic frontends for this namespace
frontends, err := str.FindDynamicFrontendsForNamespace(detail.NamespaceID, trx)
if err != nil {
dl.Warnf("error finding dynamic frontends for namespace '%v': %v", detail.NamespaceName, err)
continue
}
// send unbind mapping updates to each dynamic frontend
for _, frontend := range frontends {
frontendName := util.NameInNamespace(detail.Name, detail.NamespaceName)
if err := dPCtrl.UnbindFrontendMapping(frontend.Token, frontendName, trx); err != nil {
dl.Errorf("error unbinding frontend mapping from frontend '%v': %v", frontend.Token, err)
// continue with other frontends rather than failing completely
} else {
dl.Debugf("unbound frontend mapping '%v' from dynamic frontend '%v'", frontendName, frontend.Token)
}
}
}
return nil
}