class AppDetailSLZ(AppBaseSLZ): namespace_info = NamespaceInfoField(write_only=True, label="Namespace") chart_info = serializers.JSONField(read_only=True, label="Chart Info") chart_version = serializers.PrimaryKeyRelatedField( queryset=ChartVersion.objects.all(), write_only=True) release = ChartReleaseSLZ(read_only=True) answers = HelmValueField(initial=[], read_only=True, label="Answers", help_text="JSON format data", source="get_answers", style={ "base_template": "textarea.html", "rows": 10 }) customs = HelmValueField(initial=[], read_only=True, label="Customs", help_text="JSON format data", source="get_customs", style={ "base_template": "textarea.html", "rows": 10 }) valuefile = YamlField(initial="", read_only=True, default="", label="ValueFile", help_text="Yaml format data", source="get_valuefile", style={ "base_template": "textarea.html", "rows": 10 }) valuefile_name = serializers.CharField(source="get_valuefile_name", read_only=True) cmd_flags = serializers.JSONField(source="get_cmd_flags", read_only=True) class Meta: model = App fields = ("id", "name", "namespace", "namespace_id", "cluster_id", "namespace_info", "chart_version", "chart_info", "answers", "customs", "valuefile", "project_id", "chart", "transitioning_result", "transitioning_message", "transitioning_on", "transitioning_action", "release", "creator", "updator", "created", "updated", "valuefile_name", "cmd_flags")
class AppCreatePreviewSLZ(AppMixin, serializers.Serializer): """ 创建预览 """ name = serializers.CharField(write_only=True) namespace_info = NamespaceInfoField(write_only=True, label="Namespace") chart_version = serializers.PrimaryKeyRelatedField(queryset=ChartVersion.objects.all(), write_only=True) answers = HelmValueField( initial=[], write_only=True, label="Answers", help_text="JSON format data", source="get_answers", style={"base_template": "textarea.html", "rows": 10}, ) customs = HelmValueField( initial=[], write_only=True, label="Customs", help_text="JSON format data", source="get_customs", style={"base_template": "textarea.html", "rows": 10}, ) valuefile = YamlField( initial="", write_only=True, allow_blank=True, default="", label="ValueFile", help_text="Yaml format data", style={"base_template": "textarea.html", "rows": 10}, ) content = serializers.JSONField(read_only=True) notes = serializers.JSONField(read_only=True) cmd_flags = serializers.JSONField(required=False, default=[]) def create(self, validated_data): """ 生成应用的预览数据,这个时候应用没有创建,release也没有创建 """ namespace_info = self.get_ns_info_by_id(validated_data["namespace_info"]) cluster_id = namespace_info["cluster_id"] check_cluster_perm( user=self.context["request"].user, project_id=namespace_info["project_id"], cluster_id=cluster_id, request=self.context["request"], ) # prepare parameters parameters = merge_rancher_answers(validated_data["get_answers"], validated_data["get_customs"]) valuefile = get_valuefile_with_bcs_variable_injected( access_token=self.context["request"].user.token.access_token, project_id=namespace_info["project_id"], namespace_id=namespace_info["id"], valuefile=validated_data["valuefile"], cluster_id=cluster_id, ) # inject bcs info now = datetime.datetime.now() username = self.context["request"].user.username # 组装注入的参数 bcs_inject_data = bcs_helm_utils.BCSInjectData( source_type="helm", creator=username, updator=username, version=validated_data["chart_version"].version, project_id=self.project_id, app_id=self.context["request"].project.cc_app_id, cluster_id=cluster_id, namespace=namespace_info["name"], stdlog_data_id=bcs_helm_utils.get_stdlog_data_id(self.project_id), image_pull_secret=bcs_helm_utils.provide_image_pull_secrets(namespace_info["name"]), ) client = KubeHelmClient(helm_bin=settings.HELM3_BIN) try: extra_params = {"cmd_flags": validated_data["cmd_flags"]} content, notes = client.template_with_ytt_renderer( files=validated_data["chart_version"].files, namespace=namespace_info["name"], name=validated_data.get("name"), parameters=parameters, valuefile=valuefile, cluster_id=cluster_id, bcs_inject_data=bcs_inject_data, **extra_params ) except helm_exceptions.HelmBaseException: # raise ParseError(str(e)) # NOTE: 现阶段为防止出现未测试到的情况,允许出错时,按照先前流程渲染;后续删除 content, notes = _template_with_bcs_renderer( client, validated_data["chart_version"].files, validated_data.get("name"), namespace_info["name"], namespace_info["id"], parameters, valuefile, cluster_id, username, now, validated_data["chart_version"].version, self.access_token, self.project_id, ) return {"content": preview_parse(content, namespace_info["name"]), "notes": notes} class Meta: fields = ( "name", "namespace_info", "chart_version", "answers", "customs", "valuefile", "content", "notes", "cmd_flags", ) read_only_fields = ( "content", "notes", )
class AppReleasePreviewSLZ(AppMixin, serializers.Serializer): """ 发布预览 """ upgrade_verion = UpgradeVersionField(write_only=True, required=True) answers = HelmValueField( initial=[], write_only=True, label="Answers", help_text="JSON format data", source="get_answers", style={"base_template": "textarea.html", "rows": 10}, ) customs = HelmValueField( initial=[], write_only=True, label="Customs", help_text="JSON format data", source="get_customs", style={"base_template": "textarea.html", "rows": 10}, ) valuefile = YamlField( initial="", write_only=True, allow_blank=True, required=False, default="", label="ValueFile", help_text="Yaml format data", style={"base_template": "textarea.html", "rows": 10}, ) content = serializers.JSONField(read_only=True) notes = serializers.JSONField(read_only=True) difference = serializers.JSONField(read_only=True) chart_version_changed = serializers.BooleanField(read_only=True) old_content = serializers.JSONField(read_only=True) # 方便前端渲染 new_content = serializers.JSONField(read_only=True) def create(self, validated_data): """ 应用更新时的预览数据,这个时候目标release还没有创建 """ instance = App.objects.get(id=self.app_id) check_cluster_perm( user=self.context["request"].user, project_id=instance.project_id, cluster_id=instance.cluster_id, request=self.context["request"], ) # 标记Chart中的values.yaml是否发生变化,用于提醒用户 chart_version_changed = False # prepare parameters parameters = merge_rancher_answers(validated_data["get_answers"], validated_data["get_customs"]) chart_version_id = validated_data["upgrade_verion"] chart_version_id = int(chart_version_id) if chart_version_id == KEEP_TEMPLATE_UNCHANGED: files = instance.release.chartVersionSnapshot.files else: chart_version_changed = True chart_version = ChartVersion.objects.get(id=chart_version_id) files = chart_version.files valuefile = get_valuefile_with_bcs_variable_injected( access_token=self.context["request"].user.token.access_token, project_id=instance.project_id, namespace_id=instance.namespace_id, valuefile=validated_data["valuefile"], cluster_id=instance.cluster_id, ) now = datetime.datetime.now() username = self.context["request"].user.username # 组装注入的参数 bcs_inject_data = bcs_helm_utils.BCSInjectData( source_type="helm", creator=username, updator=username, version=instance.release.chartVersionSnapshot.version, project_id=self.project_id, app_id=self.context["request"].project.cc_app_id, cluster_id=instance.cluster_id, namespace=instance.namespace, stdlog_data_id=bcs_helm_utils.get_stdlog_data_id(self.project_id), image_pull_secret=bcs_helm_utils.provide_image_pull_secrets(instance.namespace), ) # 默认为使用helm3 client client = KubeHelmClient(helm_bin=settings.HELM3_BIN) try: content, notes = client.template_with_ytt_renderer( files=files, namespace=instance.namespace, name=instance.name, parameters=parameters, valuefile=valuefile, cluster_id=instance.cluster_id, bcs_inject_data=bcs_inject_data, ) except helm_exceptions.HelmBaseException: # raise ParseError(str(e)) # NOTE: 现阶段为防止出现未测试到的情况,允许出错时,按照先前流程渲染;后续删除 content, notes = _template_with_bcs_renderer( client, files, instance.name, instance.namespace, instance.namespace_id, parameters, valuefile, instance.cluster_id, username, now, instance.release.chartVersionSnapshot.version, self.access_token, instance.project_id, ) # compute diff old_content = instance.release.content if not old_content: old_content, _ = instance.render_app( username=self.context["request"].user.username, access_token=self.access_token ) difference = simple_diff(old_content, content, instance.namespace) return { "content": preview_parse(content, instance.namespace), "notes": notes, "difference": difference, "chart_version_changed": chart_version_changed, "old_content": old_content, "new_content": content, } class Meta: fields = ( "name", "namespace_info", "chart_version", "answers", "customs", "valuefile", "content", "notes", "difference", "chart_version_changed", ) read_only_fields = ( "content", "notes", "chart_version_changed", )
class AppUpgradeSLZ(AppBaseSLZ): upgrade_verion = UpgradeVersionField(write_only=True, required=True) answers = HelmValueField( label="Answers", help_text="JSON format data", source="get_answers", style={"base_template": "textarea.html", "rows": 10}, ) customs = HelmValueField( label="Customs", help_text="JSON format data", source="get_customs", style={"base_template": "textarea.html", "rows": 10}, ) valuefile = YamlField( initial="", default="", label="ValueFile", allow_blank=True, help_text="Yaml format data", source="get_valuefile", style={"base_template": "textarea.html", "rows": 10}, ) valuefile_name = serializers.CharField( source="get_valuefile_name", write_only=True, default=DEFAULT_VALUES_FILE_NAME ) cmd_flags = serializers.JSONField(required=False, default=[]) def update(self, instance, validated_data): # update sys variable sys_variables = collect_system_variable( access_token=self.context["request"].user.token.access_token, project_id=instance.project_id, namespace_id=instance.namespace_id, ) return instance.upgrade_app( access_token=self.access_token, chart_version_id=validated_data["upgrade_verion"], answers=validated_data["get_answers"], customs=validated_data["get_customs"], valuefile=validated_data.get("get_valuefile"), updator=self.request_username, sys_variables=sys_variables, valuefile_name=validated_data.get("get_valuefile_name"), cmd_flags=validated_data["cmd_flags"], ) class Meta: model = App fields = ( "name", "namespace", "upgrade_verion", "answers", "customs", "valuefile", "project_id", "cluster_id", "chart", "transitioning_result", "transitioning_message", "transitioning_on", "transitioning_action", "id", "valuefile_name", "cmd_flags", ) read_only_fields = ( "name", "namespace", "project_id", "cluster_id", "chart", "transitioning_result", "transitioning_message", "transitioning_on", "transitioning_action", ) extra_kwargs = { "answers": {"write_only": True}, "customs": {"write_only": True}, "valuefile": {"write_only": True}, "upgrade_verion": {"write_only": True}, "valuefile_name": {"write_only": True}, }
class AppSLZ(AppBaseSLZ): name = serializers.RegexField( RESOURCE_NAME_REGEX, error_messages={"invalid": _('不符合k8s资源名规范, 只能由小写字母数字或者-组成,正则:{}').format(RESOURCE_NAME_REGEX)}, ) namespace_info = NamespaceInfoField(write_only=True, label="Namespace") chart_version = serializers.PrimaryKeyRelatedField(queryset=ChartVersion.objects.all(), write_only=True) answers = HelmValueField( initial=[], write_only=True, label="Answers", help_text="JSON format data", source="get_answers", style={"base_template": "textarea.html", "rows": 10}, ) customs = HelmValueField( initial=[], write_only=True, label="Customs", help_text="JSON format data", source="get_customs", style={"base_template": "textarea.html", "rows": 10}, ) valuefile = YamlField( initial="", write_only=True, allow_blank=True, default="", label="ValueFile", help_text="Yaml format data", source="get_valuefile", style={"base_template": "textarea.html", "rows": 10}, ) valuefile_name = serializers.CharField( source="get_valuefile_name", write_only=True, default=DEFAULT_VALUES_FILE_NAME ) current_version = serializers.CharField(source="get_current_version", read_only=True) cmd_flags = serializers.JSONField(required=False, default=[]) def create(self, validated_data): namespace_info = self.get_ns_info_by_id(validated_data["namespace_info"]) check_cluster_perm( user=self.context["request"].user, project_id=namespace_info["project_id"], cluster_id=namespace_info["cluster_id"], request=self.context["request"], ) # 检查集群已经成功注册到 bcs, 否则让用户先完成注册逻辑 bcs_client = get_bcs_client( project_id=namespace_info["project_id"], cluster_id=namespace_info["cluster_id"], access_token=self.context["request"].user.token.access_token, ) bcs_client.get_cluster_credential() sys_variables = collect_system_variable( access_token=self.context["request"].user.token.access_token, project_id=namespace_info["project_id"], namespace_id=namespace_info["id"], ) return App.objects.initialize_app( access_token=self.access_token, name=validated_data.get("name"), project_id=self.project_id, cluster_id=namespace_info["cluster_id"], namespace_id=namespace_info["id"], namespace=namespace_info["name"], chart_version=validated_data["chart_version"], answers=validated_data["get_answers"], customs=validated_data["get_customs"], valuefile=validated_data.get("get_valuefile"), creator=self.request_username, updator=self.request_username, sys_variables=sys_variables, valuefile_name=validated_data.get('get_valuefile_name'), cmd_flags=validated_data["cmd_flags"], ) def validate_name(self, value): """make sure app name is starts with alphabet character, if the name used as resource name, numeric will forbidded by api server """ if value and (not value.islower() or not value[:1].islower()): raise serializers.ValidationError("name must starts with lower alphabet character") return value class Meta: model = App fields = ( "name", "namespace", "namespace_id", "cluster_id", "namespace_info", "chart_version", "answers", "customs", "valuefile", "project_id", "chart", "transitioning_result", "transitioning_message", "transitioning_on", "transitioning_action", "created", "updated", "creator", "updator", "current_version", "id", "valuefile_name", "cmd_flags", ) read_only_fields = ( "namespace", "namespace_id", "cluster_id", "project_id", "chart", "transitioning_result", "transitioning_message", "transitioning_on", "transitioning_action", "created", "updated", "creator", "updator", "current_version", )
class AppCreatePreviewSLZ(AppMixin, serializers.Serializer): """ 创建预览 """ name = serializers.CharField(write_only=True) namespace_info = NamespaceInfoField(write_only=True, label="Namespace") chart_version = serializers.PrimaryKeyRelatedField( queryset=ChartVersion.objects.all(), write_only=True) answers = HelmValueField(initial=[], write_only=True, label="Answers", help_text="JSON format data", source="get_answers", style={ "base_template": "textarea.html", "rows": 10 }) customs = HelmValueField(initial=[], write_only=True, label="Customs", help_text="JSON format data", source="get_customs", style={ "base_template": "textarea.html", "rows": 10 }) valuefile = YamlField(initial="", write_only=True, allow_blank=True, default="", label="ValueFile", help_text="Yaml format data", style={ "base_template": "textarea.html", "rows": 10 }) content = serializers.JSONField(read_only=True) notes = serializers.JSONField(read_only=True) def create(self, validated_data): """ 生成应用的预览数据,这个时候应用没有创建,release也没有创建 """ namespace_info = self.get_ns_info_by_id( validated_data["namespace_info"]) check_cluster_perm(user=self.context["request"].user, project_id=namespace_info["project_id"], cluster_id=namespace_info["cluster_id"], request=self.context["request"]) # prepare parameters parameters = merge_rancher_answers(validated_data["get_answers"], validated_data["get_customs"]) valuefile = get_valuefile_with_bcs_variable_injected( access_token=self.context["request"].user.token.access_token, project_id=namespace_info["project_id"], namespace_id=namespace_info["id"], valuefile=validated_data["valuefile"]) client = KubeHelmClient(helm_bin=settings.HELM_BIN) try: content, notes = client.template( files=validated_data["chart_version"].files, namespace=namespace_info["name"], name=validated_data.get("name"), parameters=parameters, valuefile=valuefile, ) except helm_exceptions.HelmBaseException as e: raise ParseError(str(e)) # inject bcs info now = datetime.datetime.now() username = self.context["request"].user.username content = bcs_info_injector.inject_bcs_info( access_token=self.access_token, project_id=self.project_id, cluster_id=namespace_info["cluster_id"], namespace_id=namespace_info["id"], namespace=namespace_info["name"], creator=username, updator=username, created_at=now, updated_at=now, resources=content, version=validated_data["chart_version"].version) return { "content": preview_parse(content, namespace_info["name"]), "notes": notes } class Meta: fields = ( "name", "namespace_info", "chart_version", "answers", "customs", "valuefile", "content", "notes", ) read_only_fields = ( "content", "notes", )
class AppReleasePreviewSLZ(AppMixin, serializers.Serializer): """ 发布预览 """ upgrade_verion = UpgradeVersionField(write_only=True, required=True) answers = HelmValueField(initial=[], write_only=True, label="Answers", help_text="JSON format data", source="get_answers", style={ "base_template": "textarea.html", "rows": 10 }) customs = HelmValueField(initial=[], write_only=True, label="Customs", help_text="JSON format data", source="get_customs", style={ "base_template": "textarea.html", "rows": 10 }) valuefile = YamlField(initial="", write_only=True, allow_blank=True, required=False, default="", label="ValueFile", help_text="Yaml format data", style={ "base_template": "textarea.html", "rows": 10 }) content = serializers.JSONField(read_only=True) notes = serializers.JSONField(read_only=True) difference = serializers.JSONField(read_only=True) chart_version_changed = serializers.BooleanField(read_only=True) old_content = serializers.JSONField(read_only=True) def create(self, validated_data): """ 应用更新时的预览数据,这个时候目标release还没有创建 """ instance = App.objects.get(id=self.app_id) check_cluster_perm(user=self.context["request"].user, project_id=instance.project_id, cluster_id=instance.cluster_id, request=self.context["request"]) # 标记Chart中的values.yaml是否发生变化,用于提醒用户 chart_version_changed = False # prepare parameters parameters = merge_rancher_answers(validated_data["get_answers"], validated_data["get_customs"]) chart_version_id = validated_data["upgrade_verion"] chart_version_id = int(chart_version_id) if chart_version_id == KEEP_TEMPLATE_UNCHANGED: files = instance.release.chartVersionSnapshot.files else: chart_version_changed = True chart_version = ChartVersion.objects.get(id=chart_version_id) files = chart_version.files valuefile = get_valuefile_with_bcs_variable_injected( access_token=self.context["request"].user.token.access_token, project_id=instance.project_id, namespace_id=instance.namespace_id, valuefile=validated_data["valuefile"]) client = KubeHelmClient(helm_bin=settings.HELM_BIN) try: content, notes = client.template( files=files, namespace=instance.namespace, name=instance.name, parameters=parameters, valuefile=valuefile, ) except helm_exceptions.HelmBaseException as e: raise ParseError(str(e)) # inject bcs info now = datetime.datetime.now() content = bcs_info_injector.inject_bcs_info( access_token=self.access_token, project_id=instance.project_id, cluster_id=instance.cluster_id, namespace_id=instance.namespace_id, namespace=instance.namespace, creator=instance.creator, updator=self.context["request"].user.username, created_at=instance.created, updated_at=now, resources=content, version=instance.release.chartVersionSnapshot.version, ) # compute diff old_content = instance.release.content if not old_content: old_content, _ = instance.render_app( username=self.context["request"].user.username, access_token=self.access_token) difference = simple_diff(old_content, content, instance.namespace) return { "content": preview_parse(content, instance.namespace), "notes": notes, "difference": difference, "chart_version_changed": chart_version_changed, "old_content": old_content } class Meta: fields = ( "name", "namespace_info", "chart_version", "answers", "customs", "valuefile", "content", "notes", "difference", "chart_version_changed", ) read_only_fields = ( "content", "notes", "chart_version_changed", )