github.com/navikt/knorten@v0.0.0-20240419132333-1333f46ed8b6/pkg/team/team.go (about) 1 package team 2 3 import ( 4 "context" 5 "database/sql" 6 "errors" 7 8 "github.com/navikt/knorten/pkg/chart" 9 "github.com/navikt/knorten/pkg/database" 10 "github.com/navikt/knorten/pkg/database/gensql" 11 "github.com/navikt/knorten/pkg/k8s" 12 "github.com/navikt/knorten/pkg/logger" 13 "k8s.io/client-go/dynamic" 14 "k8s.io/client-go/kubernetes" 15 ) 16 17 type Client struct { 18 repo *database.Repo 19 k8sClient *kubernetes.Clientset 20 k8sDynamicClient *dynamic.DynamicClient 21 gcpProject string 22 gcpRegion string 23 dryRun bool 24 } 25 26 func NewClient(repo *database.Repo, gcpProject, gcpRegion string, dryRun, inCluster bool) (*Client, error) { 27 k8sClient, err := k8s.CreateClientset(dryRun, inCluster) 28 if err != nil { 29 return nil, err 30 } 31 32 k8sDynamicClient, err := k8s.CreateDynamicClient(dryRun, inCluster) 33 if err != nil { 34 return nil, err 35 } 36 37 return &Client{ 38 repo: repo, 39 k8sClient: k8sClient, 40 k8sDynamicClient: k8sDynamicClient, 41 gcpProject: gcpProject, 42 gcpRegion: gcpRegion, 43 dryRun: dryRun, 44 }, nil 45 } 46 47 func (c Client) Create(ctx context.Context, team gensql.Team, log logger.Logger) bool { 48 log.Infof("Creating team %v", team.ID) 49 50 if retry, err := c.create(ctx, team, log); err != nil { 51 log.Info("failed creating team") 52 return retry 53 } 54 55 log.Infof("Successfully created team %v", team.ID) 56 return false 57 } 58 59 func (c Client) create(ctx context.Context, team gensql.Team, log logger.Logger) (bool, error) { 60 existingTeam, err := c.repo.TeamBySlugGet(ctx, team.Slug) 61 if err != nil && !errors.Is(err, sql.ErrNoRows) { 62 log.WithError(err).Info("failed retrieving team from database") 63 return true, err 64 } 65 66 if existingTeam.Slug == team.Slug { 67 log.Errorf("there already exists a team with name %v", team.Slug) 68 return false, err 69 } 70 71 if err := c.createGCPTeamResources(ctx, team); err != nil { 72 log.WithError(err).Info("failed creating GCP resources") 73 return true, err 74 } 75 76 namespace := k8s.TeamIDToNamespace(team.ID) 77 if err := c.createK8sNamespace(ctx, namespace); err != nil { 78 log.WithError(err).Info("failed creating team namespace") 79 return true, err 80 } 81 82 if err := c.createK8sServiceAccount(ctx, team.ID, namespace); err != nil { 83 log.WithError(err).Info("failed creating k8s service account") 84 return true, err 85 } 86 87 if err := c.repo.TeamCreate(ctx, team); err != nil { 88 log.WithError(err).Info("failed saving team to database") 89 return true, err 90 } 91 92 return false, nil 93 } 94 95 func (c Client) Update(ctx context.Context, team gensql.Team, log logger.Logger) bool { 96 log.Infof("Updating team %v", team.ID) 97 98 if retry, err := c.update(ctx, team, log); err != nil { 99 log.Info("failed updating team") 100 return retry 101 } 102 103 return false 104 } 105 106 func (c Client) update(ctx context.Context, team gensql.Team, log logger.Logger) (bool, error) { 107 err := c.repo.TeamUpdate(ctx, team) 108 if err != nil { 109 log.WithError(err).Info("failed updating team in database") 110 return true, err 111 } 112 113 namespace := k8s.TeamIDToNamespace(team.ID) 114 namespaceExists, err := c.k8sNamespaceExists(ctx, namespace) 115 if err != nil { 116 log.WithError(err).Info("failed while checking if namespace exists") 117 return true, err 118 } 119 120 if !namespaceExists { 121 if err := c.createK8sNamespace(ctx, namespace); err != nil { 122 log.WithError(err).Info("failed creating team namespace") 123 return true, err 124 } 125 } 126 127 serviceAccountExists, err := c.k8sServiceAccountExists(ctx, team.ID, namespace) 128 if err != nil { 129 log.WithError(err).Info("failed while checking if service accpunt exists") 130 return true, err 131 } 132 133 if !serviceAccountExists { 134 if err := c.createK8sServiceAccount(ctx, team.ID, namespace); err != nil { 135 log.WithError(err).Info("failed creating k8s service account") 136 return true, err 137 } 138 } 139 140 if err := c.updateGCPTeamResources(ctx, team); err != nil { 141 log.WithError(err).Info("failed while updating GCP resources") 142 return true, err 143 } 144 145 apps, err := c.repo.ChartsForTeamGet(ctx, team.ID) 146 if err != nil { 147 log.WithError(err).Infof("failed getting apps for team %v", team.ID) 148 return true, err 149 } 150 151 for _, app := range apps { 152 switch app { 153 case gensql.ChartTypeJupyterhub: 154 log.Info("Trigger update of Jupyter") 155 jupyterValues := chart.JupyterConfigurableValues{ 156 TeamID: team.ID, 157 } 158 if err := c.repo.RegisterUpdateJupyterEvent(ctx, team.ID, jupyterValues); err != nil { 159 log.WithError(err).Info("failed while registering Jupyter update event") 160 return true, err 161 } 162 case gensql.ChartTypeAirflow: 163 log.Info("Trigger update of Airflow") 164 airflowValues := chart.AirflowConfigurableValues{ 165 TeamID: team.ID, 166 } 167 if err := c.repo.RegisterUpdateAirflowEvent(ctx, team.ID, airflowValues); err != nil { 168 log.WithError(err).Info("failed while registering Airflow update event") 169 return true, err 170 } 171 } 172 } 173 174 log.Infof("Successfully updated team %v", team.Slug) 175 return false, nil 176 } 177 178 func (c Client) Delete(ctx context.Context, teamID string, log logger.Logger) bool { 179 log.Infof("Deleting team %v", teamID) 180 181 if retry, err := c.delete(ctx, teamID, log); err != nil { 182 log.Info("failed updating team") 183 return retry 184 } 185 186 log.Infof("Successfully deleted team %v", teamID) 187 return false 188 } 189 190 func (c Client) delete(ctx context.Context, teamID string, log logger.Logger) (bool, error) { 191 team, err := c.repo.TeamGet(ctx, teamID) 192 if err != nil && errors.Is(err, sql.ErrNoRows) { 193 log.WithError(err).Info("failed retrieving team from database") 194 return true, err 195 } 196 197 if err = c.deleteGCPTeamResources(ctx, team.ID); err != nil { 198 log.WithError(err).Info("failed while deleting GCP resources") 199 return true, err 200 } 201 202 if err = c.deleteK8sNamespace(ctx, k8s.TeamIDToNamespace(team.ID)); err != nil { 203 log.WithError(err).Info("failed while deleting k8s namespace") 204 return true, err 205 } 206 207 if err = c.repo.TeamDelete(ctx, team.ID); err != nil && errors.Is(err, sql.ErrNoRows) { 208 log.WithError(err).Info("failed deleting team from database") 209 return true, err 210 } 211 212 log.Info("Trigger delete of Airflow") 213 // Kun Airflow som har ressurser utenfor clusteret 214 if err := c.repo.RegisterDeleteAirflowEvent(ctx, team.ID); err != nil { 215 log.WithError(err).Info("failed while registering Airflow delete event") 216 return true, err 217 } 218 219 log.Infof("Successfully deleted team %v", teamID) 220 return false, nil 221 }